2012-02-02 8 views
2

は私が何をしようとしているのは非常に単純化した例である具象クラスにメソッド呼び出しを確認する:私のテストで部品番号 - ここ

public class Bar 
{ 
    public void SomeMethod(string param) 
    { 
     //whatever 
    } 
} 

public interface IBarRepository 
{ 
    List<Bar> GetBarsFromStore(); 
} 

public class FooService 
{ 
    private readonly IBarRepository _barRepository; 

    public FooService(IBarRepository barRepository) 
    { 
     _barRepository = barRepository; 
    } 

    public List<Bar> GetBars() 
    { 
     var bars = _barRepository.GetBarsFromStore(); 
     foreach (var bar in bars) 
     { 
      bar.SomeMethod("someValue"); 
     } 
     return bars; 
    } 
} 

、私はで定義された具体的なリストを返すようにIBarRepositoryをからかっていますユニットテストを行い、その模擬リポジトリインスタンスをFooServiceコンストラクタに渡します。

FooServiceメソッドGetBarsで、SomeMethodがリポジトリから返された各Barsに対して呼び出されたことを確認したいと考えています。私はMoqを使用しています。返されたバーのリストを嘲笑せずに(もし可能であれば)、Bar(yuck)にいくつかのハッキーフラッグを置かなくても、これを行う方法はありますか?

私はDDD帳から例を以下だが、私は、私は実装をテストに挑戦していますので、それが臭いと思うし始めているが....

改訂

答えて

3

...これは渡し:

public class Bar 
{ 
    public virtual void SomeMethod(string param) 
    { 
     //whatever 
    } 
} 

public interface IBarRepository 
{ 
    List<Bar> GetBarsFromStore(); 
} 

public class FooService 
{ 
    private readonly IBarRepository _barRepository; 

    public FooService(IBarRepository barRepository) 
    { 
     _barRepository = barRepository; 
    } 

    public List<Bar> GetBars() 
    { 
     var bars = _barRepository.GetBarsFromStore(); 
     foreach (var bar in bars) 
     { 
      bar.SomeMethod("someValue"); 
     } 
     return bars; 
    } 
} 

[TestMethod] 
public void Verify_All_Bars_Called() 
{ 
    var myBarStub = new Mock<Bar>(); 
    var mySecondBarStub = new Mock<Bar>(); 

    var myBarList = new List<Bar>() { myBarStub.Object, mySecondBarStub.Object }; 
    var myStub = new Mock<IBarRepository>(); 
    myStub.Setup(repos => repos.GetBarsFromStore()).Returns(myBarList); 
    var myService = new FooService(myStub.Object); 
    myService.GetBars(); 

    myBarStub.Verify(bar => bar.SomeMethod(It.IsAny<string>()), Times.Once()); 
    mySecondBarStub.Verify(bar => bar.SomeMethod(It.IsAny<string>()), Times.Once()); 
} 

クラスバー(SomeMethod()は仮想です)に若干の変更があります。変更はありますが、フラグを含むものはありません。:)

ここでは、より広いデザインの点で、あなたのバーで何らかの突然変異が起こっています(「SomeMethod()」が実際に行っているもの)。おそらく、FooService.GetBars()から返された各Barでこの突然変異が起こったことを確認するのが最善の方法です。つまり、いくつかのバーを返すようにリポジトリのスタブを設定し、SomeMethod()によって実行された突然変異が何であったかを確認します。結局のところ、返されるバーをコントロールするので、プレ-MyMethod()状態を設定してから、ポスト-MyMethod()状態を調べることができます。

+0

感謝を!私はバーを嘲笑する過程にあった。私はあなたがMoqでそれを行うことができてうれしいです。 – drogon

+0

助けてくれたらうれしいです。そして、具体的なクラスがパラメータのないコンストラクタを持っている限り、具体的なクラスの任意の仮想メソッド/プロパティをモックすることができます(Moqは例外をスローします) –

2

私は心の中でユニットテストでこれらのクラスを書いていた場合、私はおそらくインタフェースIBarを実装するクラスBarを持っているし、私のサービスでそのインタフェースを使用するか、またはBarで仮想SomeMethodになるだろうどちらか。

理想的には、このような:

public interface IBar 
{ 
    void SomeMethod(string param); 
} 

public class Bar : IBar 
{ 
    public void SomeMethod(string param) {} 
} 

public interface IBarRepository 
{ 
    List<IBar> GetBarsFromStore(); 
} 

public class FooService 
{ 
    private readonly IBarRepository _barRepository; 

    public FooService(IBarRepository barRepository) 
    { 
     _barRepository = barRepository; 
    } 

    public List<IBar> GetBars() 
    { 
     var bars = _barRepository.GetBarsFromStore(); 
     foreach (var bar in bars) 
     { 
      bar.SomeMethod("someValue"); 
     } 
     return bars; 
    } 
} 

次のようにその後、私のユニットテストはなります

[Test] 
public void TestSomeMethodCalledForEachBar() 
{ 
    // Setup 
    var barMocks = new Mock<IBar>[] { new Mock<IBar>(), new Mock<IBar>() }; 
    var barObjects = barMocks.Select(m => m.Object); 
    var repoList = new List<IBar>(barsObjects); 
    var repositoryMock = new Mock<IBarRepository>(); 
    repositoryMock.Setup(r => r.GetBarsFromStore()).Returns(repoList); 

    // Execute 
    var service = new FooService(repositoryMock.Object); 
    service.GetBars(); 

    // Assert 
    foreach(var barMock in barMocks) 
     barMock.Verify(b => b.SomeMethod("someValue")); 
} 
+0

テスト専用のインタフェースを追加することは、 。これはナンセンスのIFoo、IFooImplにつながります。メソッドを仮想にするだけです。 –

+0

あなたの例のような名前のインターフェイスを持つことに問題があったとは言いません。私は彼らが理由のためにそこにいると言うだろう(契約と実装の間に抽象化の層を提供する)。メソッドをバーチャルにすることで、 'DoSomething()'の実装を変更したいのであれば、 'Bar'(今は可能です)から派生しなければならないでしょう。インターフェイスにコーディングすることで、必要なものを実装するだけで、複雑さが軽減され、その後の作業がより簡単になります。 – Lukazoid

+0

IFoo、IFooImplは私のコードの匂いです。それがIAmSomeAbstractionであり、ConcreteExampleOfThatAbstractionクラスであればOKです。名前にImplの接尾辞を付けるのは意味をなさない。もちろんそれは実装です。 –