web-dev-qa-db-ja.com

Goで構造体のモックを作成する方法

TransportCarFactory構造体のモックが必要な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++などの言語では、CarFactoryMockCarMockを定義するだけでCarFactoryCar次にMakeCar()メソッドをオーバーライドしてCarMockオブジェクトを返します

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

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

Transport(new CarFactoryMock())
_

Goでこれを実現するにはどうすればよいですか?

Transport関数のプロトタイプとソースコードを変更できますが、CarFactoryCarは3番目のパッケージから取得されるため、同じに保つ必要があります =


最後のコードスニペットは、混乱を招く人間と従業員に関するものです。

15
thanhpk

私はそれを自分で考え出した。 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
_

メッセージが言うように、インターフェースからのMakeCarICarを返しますが、実際のMakeCarCarを返します。 Goはそれを許可していません。この問題を回避するには、ラッパーを定義する必要があります。手動でCarICarに変換します。

_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

11
thanhpk

インターフェイスを使用します。

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