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++などの言語では、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番目のパッケージから取得されるため、同じに保つ必要があります =
最後のコードスニペットは、混乱を招く人間と従業員に関するものです。
私はそれを自分で考え出した。 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
に、構造体ではなくインターフェイスをパラメーターとして指定する必要があります。
_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
インターフェイスを使用します。
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
構造体を1つ記述できます。
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})