如何在Go中为结构编写模拟

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})