我想为Hire
函数编写单元测试,这需要模拟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()
...
}
在Java,C#或C ++等其他OOP语言中,我可以定义扩展CarFactoryMock
和CarMock
然后覆盖CarFactory
方法的Car
和MakeCar()
返回CarMock
对象
class CarMock extends Car {
public Run() {...}
}
class CarFactoryMock extends CarFactory {
public Car MakeCar() { return new CarMock(); }
}
Transport(new CarFactoryMock())
我如何在Go中实现这一目标?
请注意,我可以更改Transport
函数的原型和源代码,但必须保持CarFactory
和Car
相同,因为它们来自第3个包
最后一段代码片段是关于人员和员工的,这导致了混乱。
答案 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{}})
答案 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})