如何在Go中为结构编写mock

时间:2016-12-09 04:45:56

标签: unit-testing go mocking

我想为Hire函数编写单元测试,这需要模拟CarFactoryCar结构。请参阅以下代码:

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

在Java,C#或C ++等其他OOP语言中,我可以定义扩展CarFactoryMockCarMock然后覆盖CarFactory方法的CarMakeCar()返回CarMock对象

class CarMock extends Car {
    public Run() {...}
}

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

Transport(new CarFactoryMock())

我如何在Go中实现这一目标?

请注意,我可以更改Transport函数的原型和源代码,但必须保持CarFactoryCar相同,因为它们来自第3个包

最后一段代码片段是关于人员和员工的,这导致了混乱。

2 个答案:

答案 0 :(得分:8)

我自己想通了。在Go中模拟结构需要更多代码,而不是支持完全后期绑定的其他OOP语言。

此代码必须单独保留,因为它取自第三方

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

由于Go仅支持接口上的后期绑定,因此我必须使Transport将接口作为参数而不是struct。

type ICar interface {
    Run()
}

type ICarFactory interface {
    MakeCar(name string) ICar
}

func Transport(cf ICarFactory) {
    ...
    car := cf.MakeCar("lamborghini")
    car.Run()
    ...
}

这是嘲笑

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

现在我可以轻松使用模拟Transport(CarFactoryMock{})。但是当我尝试调用真正的方法Transport(CarFactory {})时,go编译器会显示错误

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

正如消息所说,来自界面的MakeCar会返回ICar,但真正的MakeCar会返回Car。 Go不允许这样做。要解决此问题,我必须定义一个包装器,手动将Car转换为ICar

type CarFactoryWrapper struct {
    CarFactory
}

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

现在您可以致电Transport(CarFactoryWrapper{CarFactory{}})

以下是工作代码https://play.golang.org/p/6YyeZP4tcC

答案 1 :(得分:2)

您使用界面。

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方法接受接口Employee,然后您可以在测试中编写一个MockEmployee结构。

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