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`.
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.
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.
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})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With