Unit testing 如何在Go中为结构编写mock

Unit testing 如何在Go中为结构编写mock,unit-testing,go,mocking,Unit Testing,Go,Mocking,我想为Transport函数编写一个单元测试,它需要模拟CarFactory和Car结构。请参阅以下代码: 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) {

我想为
Transport
函数编写一个单元测试,它需要模拟
CarFactory
Car
结构。请参阅以下代码:

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

其他的OOP语言,如java、C++或C++,我可以定义<代码> CaleMyFyCK < /C> >和<代码> CalMoC< <代码>,扩展<代码> CarFactory < /C> >和<代码> Cub/Cube >然后重写<代码> MakCARARE()/Case>方法返回<代码> CARMOK/<代码>对象< /P>

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

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

Transport(new CarFactoryMock())
我如何在围棋中实现这一点

请注意,我可以更改
运输
功能的原型和源代码,但必须保持
汽车工厂
汽车
相同,因为它们来自第三个软件包


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

您使用的是一个界面

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

与支持完全后期绑定的其他OOP语言相比,在Go中模拟结构需要更多的代码

此代码必须保持不变,因为它是从第三方获取的:

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
接收接口作为参数而不是结构:

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}
}
现在我可以轻松地使用mock
传输(CarFactoryMock{})
。但是,当我尝试调用real方法
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
函数:
Transport(CarFactoryWrapper{CarFactory{}})


这是工作代码。

@sadlil您能更新您的答案以使用
Car
CarFactory
等吗。?很遗憾,在您发布答案后,OP已经更改了该选项。如果Run()方法返回两个或多个值,如Run()(字符串,错误)。我们可以重写Run()(string,error)方法,以便为不同的场景添加测试用例。类似于:键入CarMock struct{handleRun func()(字符串,错误)}和in-func(cm-CarMock)Run(){return cm.handleRun()}