How to write mock for structs in Go
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.
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})