如何断言两个列表包含在NUnit的相同的公共属性的元素?(How to assert that tw

2019-07-31 06:05发布

我想断言,两个列表中的元素包含了我所料,喜欢的东西值:

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)。 在我的测试中,我只关心公共属性值,对象不是是否相等。 我能做些什么让我的说法对吗?

Answer 1:

返工ANSWER

有一个CollectionAssert.AreEqual(IEnumerable, IEnumerable, IComparer)过载断言两个集合包含以相同的顺序相同的对象,使用IComparer实施检查对象等价。

在上述情况下,顺序并不重要。 但是,要充分也处理这种情况有两个集合的多条等价物的情况下,有必要第一顺序中的每个集合中的对象,并使用一个接一个比较,以确保也就相当于对象的数量是相同的在两个集合。

Enumerable.OrderBy提供过载,其采用IComparer<T>参数。 为了确保两个集合以相同的顺序进行排序,它是或多或少要求该类型识别属性的实现IComparable 。 下面是同时实现一个比较器类的一个例子IComparerIComparer<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);
    }
}

断言,在两个集合的对象是相同的,并配备人数相等(但不一定相同的顺序开始),以下行应该做的伎俩:

var comparer = new FooComparer();
CollectionAssert.AreEqual(
    expectedCollection.OrderBy(foo => foo, comparer), 
    foundCollection.OrderBy(foo => foo, comparer), comparer);    


Answer 2:

不,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的的收集断言的类型使用默认comparers,这在用户定义的人的大多数情况下,将对象ReferenceEquals



Answer 3:

使用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)));

现在,虽然这种说法将确保发现集合中的每个条目也存在预期的收集,并不能证明相反的,即预期的集合中每个项目包含在发现收藏。 所以,当重要的是要知道, 发现预期包含语义等价(即,它们含有相同的语义等价项),我们必须添加额外的断言。

最简单的选择是添加以下内容。

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)));

通过结合使用上述第一断言与任一第二(优选的)或第三断言,我们现在已经证明,这两个集合是语义上是相同的。



Answer 4:

您是否尝试过像成才这样?

Assert.That(foundCollection, Is.EquivalentTo(expectedCollection))


Answer 5:

要在复杂的类型,你需要实现IComaprable执行equivilance操作。

http://support.microsoft.com/kb/320727

另外,您可以使用递归的反射,这是不太理想的。



Answer 6:

一种选择是编写自定义约束进行比较的品种。 下面是关于这个问题的好文章: http://www.davidarno.org/2012/07/25/improving-nunit-custom-constraints-with-syntax-helpers/



Answer 7:

我有一个类似的问题。 清单贡献者,其中包含“提意见”和其他脂肪酶...我想所有的意见,并从中导出的创造者,但我只OFC以独特的创造者感兴趣。 如果有人创建了50点意见,我只希望她的名字出现一次。 所以我写了一个测试,看看该评论者的int GetContributors()的结果。

我可能是错的,但我觉得你以后(我是什么,当我发现这个职位后)断言,恰好有一个集合中的每个项目,在另一个集合中的一个。

我解决了这个像这样:

Assert.IsTrue(commenters.All(c => actual.Count(p => p.Id == c.Id) == 1));

如果您还想要结果列表中不包含超过预期,你可以只比较列表的长度以及其他物品..

Assert.IsTrue(commenters.length == actual.Count());

我希望这是有帮助的,如果是的话,我会很感激,如果你会率我的答案。



Answer 8:

我建议不要使用反射或任何复杂的,它只是增加了更多的工作/维护支持。

序列化对象(我建议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);

你会得到一个断言消息,如下所示:

...: “一”, “BAR2”: “B”},{ “酒吧”: “d”, “BAR2”: “d”}]

...: “一”, “BAR2”: “B”},{ “酒吧”: “C”, “BAR2”: “d”}]

... _ __ _ __ __ _ _ _ __ __ _ __ ^ __ _ _ _



Answer 9:

简单的代码说明如何使用的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);
        }
    }
}


文章来源: How to assert that two list contains elements with the same public properties in NUnit?