2016-12-09 8 views
4

CarFactoryCarの構造体をモックする必要があるHire関数の単体テストを書いてみたいと思います。 CarMockオブジェクトGoで構造体のモックを書くには

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

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

Transport(new CarFactoryMock()) 
を返すように MakeCar()メソッドをオーバーライドし、その後 CarFactoryCarを拡張することに、私はちょうど CarFactoryMockCarMockを定義することができますにするJava、C#やC++のような他のOOP言語では

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

:次のコードを参照してください。

Goでこれをどのように達成できますか?

私はTransport関数のプロトタイプとソースコードを変更することができますが、それらは第三のパッケージから取られているので、同じCarFactoryCarを維持する必要があります


最後のコードスニペットは、ヒトについてだったと従業員、混乱につながる。

+0

なぜモック 'Employee'のですか?なぜ人間?実際の従業員を「雇用」に組み込み、何が起こるかをテストするだけです。Btw:あなたは、GoでOOPベースの継承をしようとしたような臭いがあります。しないでください、あなたは自分自身を傷つけるでしょう。 – Volker

+0

従業員と人間はインターネット接続が必要な第三者APIを呼び出す可能性があるためです。ユニットテストをオフラインで実行したい – thanhpk

+0

さて、正しいことをして、サードパーティのAPIコールを設定可能にし、テスト中にこれらの呼び出しを偽物にリダイレクトします。すべてのOOPベストプラクティスが実際には実用的ではなく、特にGoで最高です。 – Volker

答えて

3

私はそれを自分で考え出しました。 Goで構造体をモックするには、完全なレイトバインドをサポートする他のOOP言語よりも多くのコードが必要です。私はTransportは、パラメータの代わりに、構造体としてのインタフェースを取る確認する必要がありますので、

このコードは、その行くので、サードパーティ

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

から取られたので、一人で残しておく必要がありますが唯一のインタフェース上の遅延バインディングをサポートします。

type ICar interface { 
    Run() 
} 

type ICarFactory interface { 
    MakeCar(name string) ICar 
} 

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

そして、ここで今、私は簡単にモックTransport(CarFactoryMock{})を使用することができますモック

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

です。しかし、私は行く、実際のメソッドトランスポート(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 

は、インターフェイスからMakeCarICarを返しますが、実際のMakeCarCarを返す、と言います。ゴーはそれを許さない。この問題を回避するには、ラップを定義する必要があります。手動で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

1

あなたはインターフェイスを使用します。

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

ハイヤーメソッドインターフェイスの従業員を受け入れると、テストで1つの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}) 
+0

このテクニックはとてもいいですが、Hire関数をもっと深く見てみると、struct Human(h.Name)のフィールドを使用していることがわかります。ヒューマンをモックするにはどうすればいいですか? – thanhpk

+0

模擬インスタンスの「人間」が実際のインスタンスの「人間」と異ならない場合。次に、あなたのMockEmploye {}を作成している間、 'Human'インスタンスに擬似データを設定します。 like 'mock:= MockEmployee {h:Human {Name:" mock-human "}}' – sadlil

+0

フィールドだけを含むHuman構造体を使って質問を短縮しました。私の本当の問題では、人間には方法があります。 – thanhpk

関連する問題