2つのリストの要素に、期待した値が含まれていると主張したいと思います。
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
//assert: I use AreEquivalent since the order does not matter
CollectionAssert.AreEquivalent(expectedCollection, foundCollection);
ただし、上記のコードは機能しません(同じ値の異なるオブジェクトに対して.Equals()がtrueを返さないためと思います)。私のテストでは、オブジェクトが等しいかどうかではなく、パブリックプロパティの値のみを考慮します。アサーションを作成するにはどうすればよいですか?
リワークされた回答
IComparer
実装を使用してオブジェクトの等価性をチェックし、2つのコレクションに同じオブジェクトが同じ順序で含まれていることをアサートするCollectionAssert.AreEqual(IEnumerable, IEnumerable, IComparer)
オーバーロードがあります。
上記のシナリオでは、順序は重要ではありません。ただし、2つのコレクションに複数の同等のオブジェクトが存在する状況でも十分に処理するには、各コレクションのオブジェクトを最初に並べ、1つずつ比較して、同等のオブジェクトの数も同じになるようにする必要があります。 2つのコレクション。
Enumerable.OrderBy
は、IComparer<T>
引数を受け取るオーバーロードを提供します。 2つのコレクションが同じ順序で並べ替えられるようにするには、識別プロパティのタイプがIComparable
を実装する必要があります。 IComparer
とIComparer<Foo>
の両方のインターフェースを実装する比較クラスの例を次に示します。ここでは、注文時にBar
が優先されると想定されています。
public class FooComparer : IComparer, IComparer<Foo>
{
public int Compare(object x, object y)
{
var lhs = x as Foo;
var rhs = y as Foo;
if (lhs == null || rhs == null) throw new InvalidOperationException();
return Compare(lhs, rhs);
}
public int Compare(Foo x, Foo y)
{
int temp;
return (temp = x.Bar.CompareTo(y.Bar)) != 0 ? temp : x.Bar2.CompareTo(y.Bar2);
}
}
2つのコレクション内のオブジェクトが同じであり、同じ数である(ただし、必ずしも最初は同じ順序である必要はない)ことを表明するには、次の行でうまくいくはずです。
var comparer = new FooComparer();
CollectionAssert.AreEqual(
expectedCollection.OrderBy(foo => foo, comparer),
foundCollection.OrderBy(foo => foo, comparer), comparer);
いいえ、NUnitには現在のようなメカニズムはありません。独自のアサーションロジックをロールする必要があります。別の方法として、またはHas.All.Matches
を利用して:
Assert.That(found, Has.All.Matches<Foo>(f => IsInExpected(f, expected)));
private bool IsInExpected(Foo item, IEnumerable<Foo> expected)
{
var matchedItem = expected.FirstOrDefault(f =>
f.Bar1 == item.Bar1 &&
f.Bar2 == item.Bar2 &&
f.Bar3 == item.Bar3
);
return matchedItem != null;
}
もちろん、これはすべての関連プロパティが事前にわかっていることを前提とします(そうでない場合、IsInExpected
はリフレクションに頼らなければなりません)。その要素の順序は関係ありません。
(そしてあなたの仮定は正しかった、NUnitのコレクションはタイプのデフォルトの比較子を使用することを表明し、ユーザー定義のもののほとんどの場合はオブジェクトのReferenceEquals
になります)
Has.All.Matches()を使用すると、foundコレクションをexpectedコレクションと比較するのに非常に効果的です。ただし、Has.All.Matches()で使用される述語を別の関数として定義する必要はありません。比較的単純な比較の場合、このように述語をラムダ式の一部として含めることができます。
Assert.That(found, Has.All.Matches<Foo>(f =>
expected.Any(e =>
f.Bar1 == e.Bar1 &&
f.Bar2 == e.Bar2 &&
f.Bar3= = e.Bar3)));
さて、このアサーションはfoundコレクションのすべてのエントリがexpectedコレクションにも存在することを保証しますが、その逆、つまり-のすべてのエントリがexpectedコレクションはfoundコレクションに含まれています。したがって、foundとexpectedが意味的に等しい(つまり、意味的に等価な同じエントリが含まれている)ことを知ることが重要な場合は、アサーションを追加する必要があります。
最も簡単な選択は、以下を追加することです。
Assert.AreEqual(found.Count() == expected.Count());
より大きなハンマーを好む人には、代わりに次のアサーションを使用できます。
Assert.That(expected, Has.All.Matches<Foo>(e =>
found.Any(f =>
e.Bar1 == f.Bar1 &&
e.Bar2 == f.Bar2 &&
e.Bar3= = f.Bar3)));
上記の最初のアサーションを2番目(推奨)または3番目のアサーションと組み合わせて使用することにより、2つのコレクションが意味的に同じであることを証明しました。
このようなことを試しましたか?
Assert.That(foundCollection, Is.EquivalentTo(expectedCollection))
複合型で等価操作を実行するには、IComaprableを実装する必要があります。
http://support.Microsoft.com/kb/320727
あるいは、あまり望ましくない再帰リフレクションを使用することもできます。
1つのオプションは、カスタム制約を記述してアイテムを比較することです。これはこの件に関する素晴らしい記事です: http://www.davidarno.org/2012/07/25/improving-nunit-custom-constraints-with-syntax-helpers/
同様の問題がありました。 「コメンター」やその他のPPLを含む寄稿者のリスト...私はすべてのコメントを取得したいので、そこからクリエーターを導きますが、私は多くの場合、ユニークなクリエーターにのみ興味があります。誰かが50件のコメントを作成した場合、私は彼女の名前を一度だけ表示させたいと思います。そこで、コメンターがGetContributors()の結果であることを確認するためのテストを作成します。
私は間違っているかもしれませんが、あなたの考え(私がこの投稿を見つけた後の私)は、あるコレクションに各項目が1つだけあり、別のコレクションにあると主張しています。
私はこれを次のように解決しました:
Assert.IsTrue(commenters.All(c => actual.Count(p => p.Id == c.Id) == 1));
結果のリストに予想外のアイテムが含まれないようにしたい場合は、リストの長さを比較することもできます。
Assert.IsTrue(commenters.length == actual.Count());
これがお役に立てば幸いです。もしそうだとしたら、私の答えを評価していただけるとありがたいです。
IComparerの使用方法を説明する単純なコード
using System.Collections;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace CollectionAssert
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
IComparer collectionComparer = new CollectionComparer();
var expected = new List<SomeModel>{ new SomeModel { Name = "SomeOne", Age = 40}, new SomeModel{Name="SomeOther", Age = 50}};
var actual = new List<SomeModel> { new SomeModel { Name = "SomeOne", Age = 40 }, new SomeModel { Name = "SomeOther", Age = 50 } };
NUnit.Framework.CollectionAssert.AreEqual(expected, actual, collectionComparer);
}
}
public class SomeModel
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CollectionComparer : IComparer, IComparer<SomeModel>
{
public int Compare(SomeModel x, SomeModel y)
{
if(x == null || y == null) return -1;
return x.Age == y.Age && x.Name == y.Name ? 0 : -1;
}
public int Compare(object x, object y)
{
var modelX = x as SomeModel;
var modelY = y as SomeModel;
return Compare(modelX, modelY);
}
}
}
リフレクションや複雑なものは使用しないことをお勧めします。単に作業やメンテナンスが増えるだけです。
オブジェクトをシリアル化し(jsonをお勧めします)、文字列を比較します。なぜあなたが注文に反対するのかはわかりませんが、各タイプのカスタム比較を保存できるので、私はそれをお勧めします。
そして、それは自動的にドメインオブジェクトの変更に対応します。
例(流暢な場合のSharpTestsEx)
using Newtonsoft.Json;
using SharpTestsEx;
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
単純な拡張機能として記述し、読みやすくすることができます。
public static class CollectionAssertExtensions
{
public static void CollectionAreEqual<T>(this IEnumerable<T> actual, IEnumerable<T> expected)
{
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
}
}
そして、あなたの例を使用してそれを次のように呼び出します:
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
foundCollection.CollectionAreEqual(foundCollection);
次のようなアサートメッセージが表示されます。
...: "a"、 "Bar2": "b"}、{"Bar": "d"、 "Bar2": "d"}]
...: "a"、 "Bar2": "b"}、{"Bar": "c"、 "Bar2": "d"}]
...__________________^- _____