Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write mock for structs in Go

I want to write a unit test for the Transport function which will require mocking CarFactory and Car structs. See the following code:

package main  type Car struct {     Name string }  func (h Car) Run() { ... }  type CarFactory struct {}  func (e CarFactory) MakeCar() Car {     return Car{} }  func Transport(cf CarFactory) {     ...     car := cf.MakeCar()     car.Run()     ... } 

In other OOP languages like Java, C# or C++, I can just define CarFactoryMock and CarMock that extend CarFactory and Car then override MakeCar() method to return a CarMock object

class CarMock extends Car {     public Run() {...} }  class CarFactoryMock extends CarFactory {     public Car MakeCar() { return new CarMock(); }                                                                                                                                                                                         }  Transport(new CarFactoryMock()) 

How do I achieve this in Go?

Note that I can change prototype and source code of Transport function, but must keep CarFactory and Car the same since they are taken from a 3rd package


The last code snippet was about Human and Employee, which lead to confusion`.

like image 359
thanhpk Avatar asked Dec 09 '16 04:12

thanhpk


People also ask

What are mocks in go?

Mocking is an essential tool to help unit test our code paths without using a real [integration] client. In Go (and other languages) it's a very understood pattern to use interfaces to achieve mocking.

How do you write mocks in C++?

Using Google Mock involves three basic steps: Use some simple macros to describe the interface you want to mock, and they will expand to the implementation of your mock class; Create some mock objects and specify its expectations and behavior using an intuitive syntax; Exercise code that uses the mock objects.


2 Answers

It takes more code to mock a struct in Go than other OOP languages that support full late binding.

This code must remain untouched since its taken from a 3rd party:

type Car struct {     Name string }  func (c Car) Run() {      fmt.Println("Real car " + c.Name + " is running") }  type CarFactory struct {}  func (cf CarFactory) MakeCar(name string) Car {     return Car{name} } 

Since Go only supports late binding on interface, I had to make Transport receive an interface as a parameter instead of a struct:

type ICar interface {     Run() }  type ICarFactory interface {     MakeCar(name string) ICar }  func Transport(cf ICarFactory) {     ...     car := cf.MakeCar("lamborghini")     car.Run()     ... } 

And here are the mocks:

type CarMock struct {     Name string }  func (cm CarMock) Run() {     fmt.Println("Mocking car " + cm.Name + " is running") }  type CarFactoryMock struct {} func (cf CarFactoryMock) MakeCar(name string) ICar {     return CarMock{name} } 

Now I can easily use the mock Transport(CarFactoryMock{}). But when I try to call the real method Transport(CarFactory{}), the go compiler shows me the following errors:

cannot use CarFactory literal (type CarFactory) as type ICarFactory in argument to Transport:     CarFactory does not implement ICarFactory (wrong type for MakeCar method)         have MakeCar(string) Car         want MakeCar(string) ICar 

As the message says, MakeCar function from the interface returns an ICar, but the real MakeCar returns a Car. Go doesn't allow that. To walk around this problem I had to define a wrapper to manually convert Car to ICar.

type CarFactoryWrapper struct {     CarFactory }  func (cf CarFactoryWrapper) MakeCar(name string) ICar {     return cf.CarFactory.MakeCar(name) } 

Now you can call the Transport function like this: Transport(CarFactoryWrapper{CarFactory{}}).

Here is the working code https://play.golang.org/p/6YyeZP4tcC.

like image 56
thanhpk Avatar answered Sep 17 '22 16:09

thanhpk


You use an interface.

type Employee interface {     GetHuman() Human }  type RealEmployee struct {     Company string     h Human }  func (e RealEmployee) GetHuman() Human {     return e.h }  // Call Hire with real employee Hire(RealEmployee{h: RealHuman}) 

Hire method accepts the interface Employee, then you can write one MockEmployee struct in your tests.

func Hire(e Employee) {     ...     h := e.GetHuman()     fmt.Println(h.Name)     ... }  // Mock Employee instance type MockEmployee struct {     Company string     h Human }  func (m MockEmployee) GetHuman() Human {     return m.h }  // Call Hire to test with mock employee Hire(MockEmployee{h: MockHuman}) 
like image 33
sadlil Avatar answered Sep 19 '22 16:09

sadlil