廣告
Unit Test 技巧:解決 Fluent Assertions BeEquivalentTo 集合比對失敗與異常處理 - 為什麼 BeEquivalentTo 測試會失敗?分析 Fluent Assertions 在比對複雜物件或集合時的常見陷阱,並提供對應的排除建議與程式碼範例。

Unit Test 技巧:解決 Fluent Assertions BeEquivalentTo 集合比對失敗與異常處理

Last updated on

日前在撰寫單元測試時,發生測試失敗,使用 Should().BeEquivalentTo(expected) 進行物件比對,已確認 待測物件期望物件 內的資料相同,但卻出現 be it misses 造成的測試結果失敗

所使用的 FluentAssertion Nuget 版本為 4.13.1

💡 版本註記:本文問題主要發生在 4.13.x 升級 5.0 之間,若您使用 v6 以上版本此問題已原生解決。若是測試觀念還不夠清晰,建議可以先參考這篇:單元測試實戰:從 Legacy Code 保護到解除過多依賴的重構技巧

問題描述

為簡化問題的本身,在測試案例中,新增 待測物件期望物件 兩個物件,且資料相同。以下是測試案列與錯誤訊息。

[Fact]
public void Test()
{
    var actual = new List<Account>
    {
        new Account {Name = "T1", Money = 100},
        new Account {Name = "T2", Money = 20}
    };
    var expected = new List<Account>
    {
        new Account {Name = "T1", Money = 100},
        new Account {Name = "T2", Money = 20}
    };
    actual.Should().BeEquivalentTo(expected);
}

public class Account
{
    public string Name { get; set; }
    public decimal Money { get; set; }
}

測試錯誤訊息如下

Xunit.Sdk.XunitException
Expected collection {

TestProject1.Account
{
   Money = 100M
   Name = "T1"
},

TestProject1.Account
{
   Money = 20M
   Name = "T2"
}} to be equivalent to {

TestProject1.Account
{
   Money = 100M
   Name = "T1"
},

TestProject1.Account
{
   Money = 20M
   Name = "T2"
}}, but it misses {

TestProject1.Account
{
   Money = 100M
   Name = "T1"
},

TestProject1.Account
{
   Money = 20M
   Name = "T2"
}}.
   at FluentAssertions.Execution.XUnit2TestFramework.Throw(String message)
   at FluentAssertions.Execution.TestFrameworkProvider.Throw(String message)
   at FluentAssertions.Execution.DefaultAssertionStrategy.HandleFailure(String message)
   at FluentAssertions.Execution.AssertionScope.FailWith(String message, Object[] args)
   at FluentAssertions.Collections.CollectionAssertions`2.BeEquivalentTo[T](IEnumerable`1 expected, String because, Object[] becauseArgs)

問題排除

查詢 Should().BeEquivalentTo,發現 Fluentassertions 已經有人反應這個 issue 🔗 了。

方案一: 變更使用的方法

在 Fluentassertions Nuget 套件不升級的條件下,將 Should().BeEquivalentTo 變更為 ShouldBeEquivalentTo() 或 ShouldAllBeEquivalentTo() 就能順利通過測試。

[Fact]
public void Test()
{
    var actual = new List<Account>
    {
        new Account {Name = "T1", Money = 100},
        new Account {Name = "T2", Money = 20}
    };

    var expected = new List<Account>
    {
        new Account {Name = "T1", Money = 100},
        new Account {Name = "T2", Money = 20}
    };

    // 以下兩種驗證方式,均通過測試
    actual.ShouldBeEquivalentTo(expected);
    actual.ShouldAllBeEquivalentTo(expected);
}

方案二: 升級到 version 5.0 以上

在反應的 Issue 🔗 的最後,FluentAssertions 🔗 專案的維護者 Dennis Dooman 也回應在 5.0 以上的版本,已修正此問題。

筆者實際升級到 5.0 之後,確實也能順利通過測試。

函數小筆記

.Should().BeEquivalentTo()

  • 可用於比對 Collection, Dictionaries
  • 針對物件再次出現(recurs)的 Field 與 Properity 進行比對。預設比對方式如同 Object.Equals

.ShouldBeEquivalentTo()

  • 因為容易與 Should().BeEquivalentTo() 搞混,所以在 5.0 之後,該方法已經被拔除。

.ShouldAllBeEquivalentTo ()

  • 在 5.0 之後,該方法已經被拔除。

小結

version 5.0 beforeversion 5.0 later
.Should().ShouldBeEquivalentTo()VV
.ShouldBeEquivalentTo()V
.ShouldAllBeEquivalentTo ()V

💡 互動時間 隨著 Fluent Assertions 版本推進到 v6、v7,你在寫單元測試時還有遇過其他框架升級的雷點或坑嗎?歡迎在下方留言交流你的除錯經驗!

參考資訊

💬 留下你的想法

有問題、不同看法,或是你踩過類似的坑?歡迎留言討論,我會盡量回覆。