如何在Go中为结构编写mock

tha*_*hpk 12 unit-testing mocking go

我想为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()
    ...
}
Run Code Online (Sandbox Code Playgroud)

在其它OOP语言如Java,C#或C++,我可以只定义CarFactoryMockCarMock延伸CarFactoryCar然后覆盖MakeCar()方法返回一个CarMock对象

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

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

Transport(new CarFactoryMock())
Run Code Online (Sandbox Code Playgroud)

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

请注意,我可以改变的原型和源代码Transport的功能,但必须保持CarFactoryCar相同的,因为他们是从第三包装中取出


最后一段代码片段是关于Human和Employee的,这导致了混乱.

tha*_*hpk 13

我自己想通了.在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}
}
Run Code Online (Sandbox Code Playgroud)

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

type ICar interface {
    Run()
}

type ICarFactory interface {
    MakeCar(name string) ICar
}

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

这是嘲笑

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}
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

正如消息所说,MakeCar从界面返回一个ICar但真实的MakeCar返回一个Car.Go不允许这样做.要解决这个问题,我必须定义一个包装器,手动转换CarICar.

type CarFactoryWrapper struct {
    CarFactory
}

func (cf CarFactoryWrapper) MakeCar(name string) ICar {
    return cf.CarFactory.MakeCar(name)
}
Run Code Online (Sandbox Code Playgroud)

现在你可以打电话了 Transport(CarFactoryWrapper{CarFactory{}})

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


sad*_*lil 9

你使用一个接口。

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})
Run Code Online (Sandbox Code Playgroud)

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})
Run Code Online (Sandbox Code Playgroud)

  • @sadlil,您可以更新您的答案以使用“Car”、“CarFactory”等吗?不幸的是,在您发布答案后,OP 改变了这一点。 (3认同)