I want to write a unit test for the Hire
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`.
I figured it out myself. It takes more code to mock a struct in Go than other OOP languages that support full late binding.
This code must leave alone since its taken from 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 support late binding on interface so I have to make Transport
take an interface as parameter instead of struct.
type ICar interface {
Run()
}
type ICarFactory interface {
MakeCar(name string) ICar
}
func Transport(cf ICarFactory) {
...
car := cf.MakeCar("lamborghini")
car.Run()
...
}
And here is 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 using the mock Transport(CarFactoryMock{})
. But when I try to call the real method Transport(CarFactory{}), go compiler shows me 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 say, MakeCar
from the interface returns an ICar
but the real MakeCar
returns a Car
. Go doesn't allow that. To walk around this problem I have to define a wrapper, 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 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})