如何在 FluentAssertions 中使用 Excluding 来排除 Dictionary 中的特定 KeyValue 对

Posted

技术标签:

【中文标题】如何在 FluentAssertions 中使用 Excluding 来排除 Dictionary 中的特定 KeyValue 对【英文标题】:How to use Excluding in FluentAssertions to exclude specific KeyValue pair in Dictionary 【发布时间】:2018-04-29 05:10:04 【问题描述】:

我正在使用 FluentAssertions 和 ShouldBeEquivalentTo 来比较两个 Dictionary<string, string> 类型的字典,但想排除一个或多个特定的 KeyValue 对(因为在这种情况下它们包含时间戳)。如何做到这一点?

我尝试过类似:opt => opt.Excluding(x => x.Single(kv => kv.Key == "MySearchKey")) 但这会导致错误,例如:Message: System.ArgumentException : Expression <Convert(x.Single(kv => (kv.Key == "MySearchKey")))> cannot be used to select a member.

我想要的可能吗?或者我应该只排除值而不是对(这可能更好,因为那时将检查密钥的存在)?谢谢!

【问题讨论】:

为什么不用linq排除键值对并比较结果 @Nkosi,感谢您的建议,我现在正在使用类似的过滤方法(如下 Jonas 所建议的)。 【参考方案1】:

Excluding() 旨在排除 type 的成员,而不是排除 collection 的成员,有关详细信息,请参阅 documentation。

注意:以下代码适用于 Fluent Assertions 的当前稳定版本 4.19.4。

示例: 您想要比较 PersonPersonDTO 的实例,但 Person 包含要从对象比较中排除的 AnotherProperty

var person = new Person

    FirstName = "John",
    LastName = "McClane",
    AnotherProperty = 42
;

var personDTO = new PersonDTO

    FirstName = "John",
    LastName = "McClane"
;

您可以在此处使用Exclude 排除某个类型的成员。

person.ShouldBeEquivalentTo(personDTO, options => options.Excluding(e => e.AnotherProperty));

在您的具体情况下,我不会使用ShouldBeEquivalentTo。 考虑这两个字典实例,您想省略 collection 的成员,这里是 Key == "unknown" 的成员。

var actual = new Dictionary<string, int>

    ["one"] = 1,
    ["two"] = 2,
    ["three"] = 3,
    ["unknown"] = -1,
    ["fail"] = -2
;

var expected = new Dictionary<string, int>

    ["one"] = 1,
    ["two"] = 2,
    ["three"] = 3
;

您可以过滤掉不需要的键值对:

IEnumerable<KeyValuePair<string, int>> filtered = actual.Where(e => e.Key != "unknown");

现在断言将在两个IEnumerable&lt;KeyValuePair&lt;string, int&gt;&gt;s之间

filtered.Should().Equal(expected);

这将给出以下断言失败消息:

FluentAssertions.Execution.AssertionFailedException: 'Expected collection to be equal to [one, 1], [two, 2], [three, 3], but [one, 1], [two, 2], [three, 3], [fail, -2] contains 1 item(s) too many.'

否则将过滤后的枚举转回字典:

Dictionary<string, int> filteredDict = actual.Where(e => e.Key != "unknown")
    .ToDictionary(e => e.Key, e => e.Value);

您现在将再次比较Dictionary&lt;string, int&gt;s:

filteredDict.Should().Equal(expected);

这将给出以下断言失败消息:

FluentAssertions.Execution.AssertionFailedException: 'Expected dictionary to be equal to [one, 1], [two, 2], [three, 3], but found additional keys "fail".'

如果想使用第二种方法并且您经常这样做,您可以创建扩展方法来提取从测试方法中删除成员的逻辑。

public static class DictionaryExtensions

    public static IDictionary<TKey, TValue> ExceptKeys<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, params TKey[] keys)
    
        if (dictionary == null) throw new ArgumentNullException(nameof(dictionary));
        if (keys == null) throw new ArgumentNullException(nameof(keys));

        return dictionary.Where(e => !keys.Contains(e.Key)).ToDictionary(e => e.Key, e => e.Value);
    

    public static IDictionary<TKey, TValue> ExceptValues<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, params TValue[] values)
    
        if (dictionary == null) throw new ArgumentNullException(nameof(dictionary));
        if (values == null) throw new ArgumentNullException(nameof(values));

        return dictionary.Where(e => !values.Contains(e.Value)).ToDictionary(e => e.Key, e => e.Value);
    

你现在可以写一个我认为更清晰简洁的测试:

actual.ExceptKeys("unknown").Should().Equal(expected);

【讨论】:

乔纳斯,感谢您非常详尽的解释!我习惯了 ShouldBeEquivalentTo,因为我大部分时间都将对象与对象进行比较。比较字典时,应该().Equal() 确实要好得多。我也切换到你的过滤解决方案。不错的方法,它完全适合我的需求。再次感谢! 请注意,ExceptKeys 和 exceptValues 应谨慎使用,因为它们不会保留原始字典的字典键的相等比较器。

以上是关于如何在 FluentAssertions 中使用 Excluding 来排除 Dictionary 中的特定 KeyValue 对的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 FluentAssertions 在 XUnit 中测试 MediatR 处理程序

如何在 FluentAssertions 中为集合中的属性使用排除?

如何在 FluentAssertions 中使用 Excluding 来排除 Dictionary 中的特定 KeyValue 对

如何使用 FluentAssertions 4.x 版断言异常?

如何使用FluentAssertions在XUnit中测试MediatR处理程序

FluentAssertions:如何在每对元素上使用自定义比较来比较两个集合?