web-dev-qa-db-ja.com

Assert.AreEqualは2つのジェネリックIEnumerable間の等価性をどのように決定しますか?

メソッドが正しいIEnumerableを返すかどうかを確認する単体テストがあります。このメソッドは、yield returnを使用して列挙型を構築します。列挙可能なクラスは次のとおりです。

enum TokenType
{
    NUMBER,
    COMMAND,
    ARITHMETIC,
}

internal class Token
{
    public TokenType type { get; set; }
    public string text { get; set; }
    public static bool operator == (Token lh, Token rh) { return (lh.type == rh.type) && (lh.text == rh.text); }
    public static bool operator != (Token lh, Token rh) { return !(lh == rh); }
    public override int GetHashCode()
    {
        return text.GetHashCode() % type.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        return this == (Token)obj;
    }
}

これはメソッドの関連部分です:

 foreach (var lookup in REGEX_MAPPING)
 {
     if (lookup.re.IsMatch(s))
     {
         yield return new Token { type = lookup.type, text = s };
         break;
     }
 }

このメソッドの結果をactualに保存する場合、別の列挙可能なexpectedを作成し、次のように比較します...

  Assert.AreEqual(expected, actual);

...、アサーションは失敗します。

PythonのIEnumerable関数 に類似したZipの拡張メソッドを作成し(2つのIEnumerableを組み合わせてペアのセットにします)、これを試しました。

foreach(Token[] t in expected.Zip(actual))
{
    Assert.AreEqual(t[0], t[1]);
}

出来た!では、これら2つのAssert.AreEqualsの違いは何ですか?

35
Jason Baker

Assert.AreEqualは、手元にある2つのオブジェクトを比較します。 IEnumerablesはそれ自体が型であり、一部のコレクションを反復処理するメカニズムを提供しますが、実際にはそのコレクションではありません。元の比較では2つのIEnumerablesを比較しましたが、これは有効な比較ですが、必要なものではありません。 2つのIEnumerablesが列挙するものを比較する必要がありました。

これが2つの列挙型を比較す​​る方法です。

Assert.AreEqual(t1.Count(), t2.Count());

IEnumerator<Token> e1 = t1.GetEnumerator();
IEnumerator<Token> e2 = t2.GetEnumerator();

while (e1.MoveNext() && e2.MoveNext())
{
    Assert.AreEqual(e1.Current, e2.Current);
}

上記があなたの.Zipメソッドよりもコードが少ないかどうかはわかりませんが、それは非常に簡単です。

25
jrista

それを見つけた:

Assert.IsTrue(expected.SequenceEqual(actual));
88
Jason Baker

代わりにCollectionAssertクラスを使用することを検討しました...コレクションで等価チェックを実行することを意図していると考えていますか?

補遺:
比較される「コレクション」が列挙型である場合、単純にそれらを「new List<T>(enumeration)」でラップすることが比較を実行する最も簡単な方法です。もちろん、新しいリストを作成するとオーバーヘッドが発生しますが、単体テストのコンテキストでは、これはそれほど問題にならないと思いますか?

48
jerryjvl

あなたが望む平等を主張する最も簡単で明確な方法は、jerryjvlによる回答とMEMarkによる彼の投稿へのコメントを組み合わせることです-CollectionAssert.AreEqual拡張メソッドあり:

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());

これにより、OPによって提案されたSequenceEqual回答よりも豊富なエラー情報が提供されます(予期されなかった要素が見つかったことがわかります)。例えば:

IEnumerable<string> expected = new List<string> { "a", "b" };
IEnumerable<string> actual   = new List<string> { "a", "c" }; // mismatching second element

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
// Helpful failure message!
//  CollectionAssert.AreEqual failed. (Element at index 1 do not match.)    

Assert.IsTrue(expected.SequenceEqual(actual));
// Mediocre failure message:
//  Assert.IsTrue failed.   

reallyテストが失敗した場合や、テストが失敗した場合にこのように実行できたことを喜ばしく思います-デバッガをブレークアウトしなくても、何が問題なのかを知ることさえできます。最初に失敗するテストを書きますよね? ;-)

AreEquivalentを使用して等価性をテストしている場合、エラーメッセージはさらに役立ちます(順序は関係ありません)。

CollectionAssert.AreEquivalent(expected.ToList(), actual.ToList());
// really helpful error message!
//  CollectionAssert.AreEquivalent failed. The expected collection contains 1
//  occurrence(s) of <b>. The actual collection contains 0 occurrence(s).   
19
bacar