3

優れたモッキングフレームワーク MoQ を使用して作業していると、やや驚くべき側面に遭遇しました (そして、私は驚きが好きではありません)。次のように、メソッド呼び出しの後にコレクションに追加する必要があるクラスをモックアウトしています。

public class SomeClass{

}

public class Container {
    private List<SomeClass> classes = new List<SomeClass>();

    public IEnumerable<SomeClass> Classes {
        get {
            return classes;
        }
    }

    public void addSomeClass(SomeClass instance) {
        classes.Add(instance);
    }
}

[Test]
public void ContainerContainsAddedClassAfterAdd() {
    var mockSomeClass = new Mock<SomeClass>();  
    mockSomeClass.Setup(c => c.Equals(mockSomeClass.Object)).Return(true);

    var Container = new Container();
    Container.addSomeClass(mockSomeClass.Object);

    Assert(Container.Classes.Contains(mockSomeClass.Object));
}

これはうまく機能し、モックがContainerのコレクションに追加されEquals、モックのメソッドのセットアップにより、確実にIEnumerable.Contains()true が返されます。ただし、常に複雑な問題があります。私が実際に嘲笑しているクラスは、私たちの ほど単純ではありませんSomeClass。それは次のようなものです:

public class SomeClassOverridingEquals{
    public virtual Equals(SomeClassOverridingEquals other) {
        return false;   
    }

    public override Equals(object obj) {
        var other = obj as SomeClassOverridingEquals;

        if (other != null) return Equals(other); // calls the override
        return false;
    }
}

[Test]
public void ContainerContainsAddedClassOverridingEqualsAfterAdd() {
    var mockSomeClass = new Mock<SomeClassOverridingEquals>();  
    mockSomeClass.Setup(c => c.Equals(mockSomeClass.Object)).Return(true);

    var Container = new Container();
    Container.addSomeClass(mockSomeClass.Object);

    Assert(Container.Classes.Contains(mockSomeClass.Object)); // fails
}

クラスには、独自の特定の型の Equals メソッドのオーバーライドが含まれておりSetup、モックのメソッドはその特定のメソッドをモックアウトできないようです (より一般的な をオーバーライドするだけEquals(object)です)。したがって、テストは失敗します。

これまでのところ、オーバーライドする equals を使用しないようにクラスを書き直す以外に、この非常に一般的なパターンに対処する方法を見つけていません。

私はそれが好きではありません。

誰にもアイデアはありますか?

4

4 に答える 4

6

問題は Moq ではなく、Contains 拡張メソッドにあると思います。より具体的なオーバーロードで Equals をオーバーロードした場合でも、Enumerable.Contains はList<T>.Contains、Classes プロパティが実際にはList<T>.

List<T>.ContainsこれはEqualityComparer<T>.Default.Equalsデフォルトで System.Object から継承された Equals メソッドを呼び出すと思います。つまり、モックがオーバーライドするものではなく、System.Object を入力として受け取るものです。

Reflector での の実装を熟読すると、 を実装EqualityComparer<T>.Defaultする型には特別なケースがあるように思われるIEquatable<T>ため、そのインターフェイスをクラスに追加すると (適切なメソッドが既に存在します)、動作が異なる場合があります。

于 2009-09-25T09:41:53.137 に答える
1

のインスタンスを作成するのはどれくらい難しいSomeClassですか?実際のオブジェクトを使用するだけの方が理にかなっていますか?可能であれば、コンテナとクラスの間の関係の一部である特定の動作を変更しないので、それはより良いでしょう。

または、返されたコレクションを繰り返し処理して、モックインスタンスと同じオブジェクトを探すこともできます。これにより、特殊な動作を回避する必要がなくなります。

于 2009-10-04T22:44:56.443 に答える
1

マーク・シーマンは正しいです。適切なオーバーロードで Moq をほのめかしていることを確認する必要があります。

mockSomeClass.Setup(c => c.Equals((object)mockSomeClass.Object)).Return(true);
于 2009-09-28T12:33:26.467 に答える
0

インターフェイスを作成し、ISomeClass代わりにコンテナがインターフェイスを使用するようにします。このようにして、次の 2 つのことを達成できます。

  1. 簡単にモックしてMock<ISomeClass>、実際にコンテナの機能を単体テストするだけです
  2. SomeClass クラス機能の実際の実装のテストとは別に、実際の単体テスト コンテナーを使用して、単体テストをより適切に行います。
于 2009-09-25T09:15:07.997 に答える