簡體   English   中英

如何斷言兩個列表包含在 NUnit 中具有相同公共屬性的元素?

[英]How to assert that two list contains elements with the same public properties in NUnit?

我想斷言兩個列表的元素包含我期望的值,例如:

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)。 在我的測試中,我只關心公共屬性值,而不關心對象是否相等。 我能做些什么來做出我的斷言?

重新設計的答案

有一個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);    

不,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() 可以很好地將找到的集合與預期的集合進行比較。 但是,不必將 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)));

現在,雖然此斷言將確保找到的集合中的每個條目也存在於預期的集合中,但它並不能證明相反,即預期的集合中的每個條目都包含在找到的集合中。 因此,當知道foundexpected contains 在語義上是等效的(即,它們包含相同的語義等效條目)很重要時,我們必須添加一個額外的斷言。

最簡單的選擇是添加以下內容。

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

通過將上面的第一個斷言與第二個(首選)或第三個斷言結合使用,我們現在已經證明這兩個集合在語義上是相同的。

你有沒有嘗試過這樣的事情?

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

我有一個類似的問題。 列出貢獻者,其中包含“評論者”和其他人...我想獲得所有評論並從中得出創作者,但我只對獨特的創作者感興趣。 如果有人創建了 50 條評論,我只希望她的名字出現一次。 所以我寫了一個測試來查看評論者是否在 GetContributors() 結果中。

我可能是錯的,但我認為你的追求(當我發現這篇文章時我所追求的)是斷言一個集合中的每個項目中都有一個,在另一個集合中找到。

我是這樣解決的:

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

如果您還希望結果列表不包含預期之外的其他項目,您也可以比較列表的長度。

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

我希望這是有幫助的,如果是這樣,如果您對我的回答進行評分,我將不勝感激。

要對復雜類型執行等效操作,您需要實現 IComaprable。

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

或者,您可以使用遞歸反射,這不太理想。

一種選擇是編寫自定義約束來比較項目。 這是一篇關於這個主題的好文章: http : //www.davidarno.org/2012/07/25/improving-nunit-custom-constraints-with-syntax-helpers/

我建議不要使用反射或任何復雜的東西,它只會增加更多的工作/維護。

序列化對象(我推薦json)並字符串比較它們。 我不確定您為什么反對訂購,但我仍然推薦它,因為它會為每種類型保存自定義比較。

它會自動與域對象更改一起工作。

示例(SharpTestsEx 用於 fluent)

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"}]

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

解釋如何使用 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);
        }
    }
}

這使用NUnitCore程序NUnitCore NUnit 的Assertion類解決了我的問題:

AssertArrayEqualsByElements(list1.ToArray(), list2.ToArray());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM