3

ここで正しいアプローチがあるかどうかを理解するのに苦労しています。リポジトリをテストしたい。リポジトリは DbContext に依存しています。リポジトリが、DbContext のメンバーである IDbSet 型のプロパティに対して関数 Add を呼び出さなかったことを確認できるようにしたいと考えています。

私は2つのアプローチを試みました。動作で検証し、状態で検証します。スタブ化された状態が偽のオブジェクトで何をしているのか誰が知っているので、振る舞いを検証するのは正しいようです。

    public void VerifyBehaviour()
    {
        // Arrange
        var stubEntities = MockRepository.GenerateStub<IWsStatContext>();
        var stubManufcturers = MockRepository.GenerateStub<IDbSet<Manufacturer>>();
        var manufacturer = new Manufacturer() { Name = "Dummy" };
        var manufacturers = new List<Manufacturer>();
        manufacturers.Add(manufacturer);

        stubManufcturers.Stub(x => x.Local).Return(new System.Collections.ObjectModel.ObservableCollection<Manufacturer>(manufacturers));
        stubManufcturers.Stub(x => x.SingleOrDefault(m => m.Name == "Dummy")).Return(manufacturer);
        stubEntities.Manufacturers = stubManufcturers;

        // Act
        var sut = new EquiptmentRepository(stubEntities);
        sut.AddManufacturer(manufacturer);

        // Assert
        stubManufcturers.AssertWasNotCalled(x => x.Add(manufacturer));
    }


    public void VerifyState()
    { 
        // Arrange
        var stubEntities = MockRepository.GenerateStub<IWsStatContext>();
        var stubManufacturers = new InMemoryDbSet<Manufacturer>();
        var manufacturer = new Manufacturer() { Name = "Dummy" };
        stubManufacturers.Add(manufacturer);
        stubEntities.Manufacturers = stubManufacturers;

        // Act
        var sut = new EquiptmentRepository(stubEntities);
        sut.AddManufacturer(manufacturer);

        // Assert
        Assert.AreEqual(stubManufacturers.Count(), 1);
    }

SingleOrDefault のスタブ周辺で NullReferenceExceptions が発生し、動作アプローチが失敗することを確認します。そのため、状態を確認して偽の DbSet を使用するのが最善であると言っている投稿を見つけました。しかし、偽のオブジェクトの状態をチェックするのは気分が悪いです。Add 関数が実際のものとは異なる方法で実装されていたらどうなるでしょうか (元は実装されていて、リポジトリが壊れていてもテストはパスしていました)。

Add が呼び出されたことを確認できるように、SingleOrDefault をスタブする方法を知っている人はいますか? Add がライノモック以外のスタブで呼び出されたことを確認できません。

ありがとう

4

3 に答える 3

3

jimmy_keen の回答で述べたように:

SingleOrDefaultIEnumerable<T>IDbSet<T>実装する)で定義された拡張メソッドです。拡張メソッドであるということは、それが静的メソッドであることを意味します。RhinoMocks (またはその他の無料ツール) は、静的メソッドをモック/スタブすることはできません。

拡張メソッドを「スタブ化」しようとするのではなく、拡張メソッドが構築されている基になるインターフェイスをスタブ化してみてください。IEnumerable<T>

stubManufcturers.Stub( x => x.GetEnumerator() ).Return( new List<Manufacturer> { manufacturer }.GetEnumerator() );

GetEnumerator()whenが呼び出されたときの動作をスタブSingleOrDefault化することで、偽の列挙に対して期待どおりに実行され、テストで動作を評価できるようになります。

于 2012-05-10T21:30:04.037 に答える
1

SingleOrDefaultIEnumerable<T>IDbSet<T>実装する)で定義された拡張メソッドです。拡張メソッドであるということは、静的メソッドであることを意味します。RhinoMocks (またはその他の無料ツール)は静的メソッドをモック/スタブできません

残念ながら、ここには多くのオプションがありません。状態ベースの検証を行うか、手作りのモックを作成してテスト用に手動で設定する必要があります (ただし、これは状態ベースの検証で終わる可能性が最も高いです。繰り返しますが、実際にはスタブできませんSingleOrDefault)。

編集:抽出とオーバーライドの例:

まず、クラスの問題のある部分を抽出して、後でオーバーライドされるメソッドを分離する必要があります。この問題のある部分は、当然 との相互作用IDbSetです:

public class EquiptmentRepository
{
    public void Add(Manufacturer m)
    {
        // perform some local logic before calling IDbSet.Add
        this.AddToDbSet(m);
    }

    protected virtual AddToDbSet(Manufacturer m)
    {
        this.context.Manfuacturers.Add(m);
    }
}

ここで、テスト可能なバージョンでEquiptmentRepositoryオーバーライドAddToDbSetして、テストを簡単にします。たとえば、状態の検証を追加するだけです。

internal void TestableEquiptmentRepository: EquiptmentRepository
{
    internal List<Manufacturer> AddedManufacturers = new List<Manufacturer>();

    protected override void AddToDbSet(Manufacturer m)
    {
        // we're no longer calling DbSet.Add but kind of rolling
        // our own basic mock and tracking what objects were
        // add by simply adding them to internal list
        this.AddedManufacturers.Add(m);
    }
}

が呼び出されるたびに、実際Addに追加される場合は、渡されたメーカーがリストに追加されDbSetます。テストでは、クラス状態のテスト可能なバージョンを簡単に確認できます。

[Test]
public void AddManufacturer_DoesNotAddExistingManufacturersToDbSet()
{
    // Arrange
    var stubEntities = MockRepository.GenerateStub<IWsStatContext>();
    var stubManufacturers = MockRepository.GenerateStub<IDbSet<Manufacturer>>();
    var manufacturer = new Manufacturer() { Name = "Dummy" };
    stubManufacturers.Add(manufacturer);
    stubEntities.Manufacturers = stubManufacturers;

    // Act
    var sut = new TestableEquiptmentRepository(stubEntities);
    sut.AddManufacturer(manufacturer);

    // Assert
    Assert.AreEqual(sut.AddedManufacturers.Count(), 0);
}

EquiptmentRepository.Addこのようにして、まったく対話する必要なく、すべてのロジックを検証できますDbSet

于 2012-05-09T23:24:41.223 に答える
0

RhinoMocks または Moq で静的メソッドをモックすることはできません (TypeMock はできます)。

偽のオブジェクトの状態検証は、テスト対象のシステムではなく、実際にモックを検証します。

コードをテスト可能にする手法があります (ただし、価格が高すぎると思います)。インターフェイスに拡張メソッドを抽出し、System.Linq.Enumerable 拡張機能の使用を独自のものに置き換える必要があります。

var item = items.MyExtensions().SingleOrDefault();

ところで、静的メソッドのモックに直面したときは、通常、次のいずれかを行います。

  • 静的メソッドの実行結果を渡します。たとえば、テストされたメソッドで現在の日付が必要な場合は、呼び出す代わりにDateTime.Today現在の時刻を parameter として渡しますFoo(DateTime date)
  • 静的呼び出しを非静的オブジェクトにラップします。たとえば、構成設定を取得する必要がある場合は、 を呼び出す代わりに、すべての作業を静的 ConfigurationManager に委譲する (および interface を実装する)ConfigurationManager.AppSettings["foo"]非静的クラスを作成します。BarConfigurationIBar { int Foo { get; }
  • 嘲笑しないでください。テストすべきではないものをテストしているように見えます (列挙可能な拡張機能、ログなど)

リポジトリを単体テストしないことを検討してください。データ アクセス ロジックの統合テストは、はるかに理にかなっています。

于 2012-05-10T09:52:35.837 に答える