C# 如何断言两个列表包含在 NUnit 中具有相同公共属性的元素?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12171411/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to assert that two list contains elements with the same public properties in NUnit?
提问by Louis Rhys
I want to assert that the elements of two list contains values that I expected, something like:
我想断言两个列表的元素包含我期望的值,例如:
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);
However the above code will not work (I guess because .Equals() does not return true for different objects with the same value). In my test, I only care about the public property values, not whether the objects are equal. What can I do to make my assertion?
但是,上面的代码将不起作用(我猜是因为 .Equals() 对于具有相同值的不同对象不会返回 true)。在我的测试中,我只关心公共属性值,而不关心对象是否相等。我能做些什么来做出我的断言?
采纳答案by Anders Gustafsson
REWORKED ANSWER
重新设计的答案
There is a CollectionAssert.AreEqual(IEnumerable, IEnumerable, IComparer)overload to assert that two collections contain the same objects in the same order, using an IComparerimplementation to check the object equivalence.
有一个CollectionAssert.AreEqual(IEnumerable, IEnumerable, IComparer)重载来断言两个集合以相同的顺序包含相同的对象,使用一个IComparer实现来检查对象的等效性。
In the scenario described above, the order is not important. However, to sufficiently handle also the situation where there are multiple equivalent objects in the two collections, it becomes necessary to first order the objects in each collection and use one-by-one comparison to ensure that also the number of equivalent objects are the same in the two collections.
在上述场景中,顺序并不重要。然而,为了充分处理两个集合中有多个等价对象的情况,有必要首先对每个集合中的对象进行排序并使用一一比较以确保等价对象的数量也相同在两个集合中。
Enumerable.OrderByprovides an overload that takes an IComparer<T>argument. To ensure that the two collections are sorted in the same order, it is more or less required that the types of the identifying properties implement IComparable. Here is an example of a comparer class that implements both the IComparerand IComparer<Foo>interfaces, and where it is assumed that Bartakes precedence when ordering:
Enumerable.OrderBy提供一个带IComparer<T>参数的重载。为了确保两个集合以相同的顺序排序,或多或少需要标识属性的类型实现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);
}
}
To assert that the objects in the two collections are the same and comes in equal numbers (but not necessarily in the same order to begin with), the following lines should do the trick:
要断言两个集合中的对象相同并且数量相等(但不一定以相同的顺序开始),以下几行应该可以解决问题:
var comparer = new FooComparer();
CollectionAssert.AreEqual(
expectedCollection.OrderBy(foo => foo, comparer),
foundCollection.OrderBy(foo => foo, comparer), comparer);
回答by Slappy
To perform equivilance operations on complex types you need to implement IComaprable.
要对复杂类型执行等效操作,您需要实现 IComaprable。
http://support.microsoft.com/kb/320727
http://support.microsoft.com/kb/320727
Alternatively you could use recursive reflection, which is less desirable.
或者,您可以使用递归反射,这不太理想。
回答by Erwin
Have you tried something like this?
你有没有尝试过这样的事情?
Assert.That(expectedCollection, Is.EquivalentTo(foundCollection))
回答by k.m
No, NUnit has no such mechanism as of current state. You'll have to roll your own assertion logic. Either as separate method, or utilizing Has.All.Matches:
不,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;
}
This of course assumes you know all relevant properties upfront (otherwise, IsInExpectedwill have to resort to reflection) and that element order is not relevant.
这当然假设您预先知道所有相关属性(否则,IsInExpected将不得不求助于反射)并且元素顺序不相关。
(And your assumption was correct, NUnit's collection asserts use default comparers for types, which in most cases of user defined ones will be object's ReferenceEquals)
(并且您的假设是正确的,NUnit 的集合断言使用类型的默认比较器,在大多数情况下,用户定义的类型将是 object's ReferenceEquals)
回答by Scroog1
One option is to write custom constraints to compare the items. Here's a nice article on the subject: http://www.davidarno.org/2012/07/25/improving-nunit-custom-constraints-with-syntax-helpers/
一种选择是编写自定义约束来比较项目。这是一篇关于这个主题的好文章:http: //www.davidarno.org/2012/07/25/improving-nunit-custom-constraints-with-syntax-helpers/
回答by Choco Smith
I recommend against using reflection or anything complex, it just adds more work/maintenace.
我建议不要使用反射或任何复杂的东西,它只会增加更多的工作/维护。
Serialize the object (i recommend json) and string compare them. I'm unsure why you object to order by but I'd still recommend it as it will save a custom compare's for each type.
序列化对象(我推荐json)并字符串比较它们。我不确定您为什么反对订购,但我仍然推荐它,因为它会为每种类型保存自定义比较。
And it automatically works with domain objects change.
它会自动与域对象更改一起工作。
Example (SharpTestsEx for fluent)
示例(SharpTestsEx 用于 fluent)
using Newtonsoft.Json;
using SharpTestsEx;
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
You can write it as a simple extensions and make it more readable.
您可以将其编写为简单的扩展并使其更具可读性。
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));
}
}
and then using your example call it like so:
然后使用您的示例调用它,如下所示:
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
foundCollection.CollectionAreEqual(foundCollection);
You'll get an assert message like so:
你会得到这样的断言消息:
...:"a","Bar2":"b"},{"Bar":"d","Bar2":"d"}]
...:"a","Bar2":"b"},{"Bar":"d","Bar2":"d"}]
...:"a","Bar2":"b"},{"Bar":"c","Bar2":"d"}]
...:"a","Bar2":"b"},{"Bar":"c","Bar2":"d"}]
...__________________^_____
... ___ ___ ___ ___ ___ ___^ ___ __
回答by RickC
Using Has.All.Matches() works very well for comparing a foundcollection to the expectedcollection. However, it is not necessary to define the predicate used by Has.All.Matches() as a separate function. For relatively simple comparisons, the predicate can be included as part of the lambda expression like this.
使用 Has.All.Matches() 可以很好地将找到的集合与预期的集合进行比较。但是,不必将 Has.All.Matches() 使用的谓词定义为单独的函数。对于相对简单的比较,可以像这样将谓词作为 lambda 表达式的一部分包含在内。
Assert.That(found, Has.All.Matches<Foo>(f =>
expected.Any(e =>
f.Bar1 == e.Bar1 &&
f.Bar2 == e.Bar2 &&
f.Bar3= = e.Bar3)));
Now, while this assertion will ensure that every entry in the foundcollection also exists in the expectedcollection, it does not prove the reverse, namely that every entry in the expectedcollection is contained in the foundcollection. So, when it is important to know that foundand expectedcontain are semantically equivalent (i.e., they contain the same semantically equivalent entries), we must add an additional assertion.
现在,虽然此断言将确保找到的集合中的每个条目也存在于预期的集合中,但它并不能证明相反,即预期的集合中的每个条目都包含在找到的集合中。因此,当知道found和expectedcontains 在语义上是等效的(即,它们包含相同的语义等效条目)很重要时,我们必须添加一个额外的断言。
The simplest choice is to add the following.
最简单的选择是添加以下内容。
Assert.AreEqual(found.Count() == expected.Count());
For those who prefer a bigger hammer, the following assertion could be used instead.
对于那些喜欢更大锤子的人,可以使用以下断言代替。
Assert.That(expected, Has.All.Matches<Foo>(e =>
found.Any(f =>
e.Bar1 == f.Bar1 &&
e.Bar2 == f.Bar2 &&
e.Bar3= = f.Bar3)));
By using the first assertion above in conjunction with either the second (preferred) or third assertion, we have now proven that the two collections are semantically the same.
通过将上面的第一个断言与第二个(首选)或第三个断言结合使用,我们现在已经证明这两个集合在语义上是相同的。
回答by Dizzypointed
I had a similar problem. Listing contributors, which contains "commenters" and other ppl... I want to get all the comments and from that derive the creators, but I'm ofc only interested in unique creators. If someone created 50 comments I only want her name to appear once. So I write a test to see that the commenters are int the GetContributors() result.
我有一个类似的问题。列出贡献者,其中包含“评论者”和其他人...我想获得所有评论并从中得出创作者,但我只对独特的创作者感兴趣。如果有人创建了 50 条评论,我只希望她的名字出现一次。所以我写了一个测试来查看评论者是否在 GetContributors() 结果中。
I may be wrong, but what I think your after (what I was after when I found this post) is to assert that there are exactly one of each item in one collection, found in another collection.
我可能是错的,但我认为你的追求(当我发现这篇文章时我所追求的)是断言一个集合中的每个项目中都有一个,在另一个集合中找到。
I solved this like so:
我是这样解决的:
Assert.IsTrue(commenters.All(c => actual.Count(p => p.Id == c.Id) == 1));
If you also want the resulting list not to contain other items than expected you could just compare the length of the lists as well..
如果您还希望结果列表不包含预期之外的其他项目,您也可以比较列表的长度。
Assert.IsTrue(commenters.length == actual.Count());
I hope this is helpful, if so, I'd be very grateful if you would rate my answer.
我希望这是有帮助的,如果是这样,如果您对我的回答进行评分,我将不胜感激。
回答by Shishira M J
Simple code explaining how to use the IComparer
解释如何使用 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);
}
}
}
回答by user12855312
This solved my problem using NUnit's Assertionclass from the NUnitCoreassembly:
这使用程序集中的 NUnitAssertion类解决了我的问题NUnitCore:
AssertArrayEqualsByElements(list1.ToArray(), list2.ToArray());

