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

Posted

技术标签:

【中文标题】FluentAssertions:如何在每对元素上使用自定义比较来比较两个集合?【英文标题】:FluentAssertions: How to compare two collections using a custom comparison on each pair of elements? 【发布时间】:2019-09-17 12:33:19 【问题描述】:

给定以下输入:

var customers = new[] 
    new Customer  Name = "John", Age = 42 ,
    new Customer  Name = "Mary", Age = 43 
;
var employees = new[] 
    new Employee  FirstName = "John", Age = 42 ,
    new Employee  FirstName = "Mary", Age = 43 
;

使用 FluentAssertions 比较这些列表的最佳方法是什么?

我目前唯一的方法是这样的——非常类似于Enumerable.SequenceEqual:

using (var customerEnumerator = customers.GetEnumerator())
using (var employeeEnumerator = employees.GetEnumerator())

    while (customerEnumerator.MoveNext())
    
        employeeEnumerator.MoveNext().Should().BeTrue();
        var (customer, employee) = (customerEnumerator.Current, employee.Current);

        customer.Name.Should().BeEquivalentTo(employee.FirstName);
        customer.Age.Should().Be(employee.Age);
    
    employeeEnumerator.MoveNext().Should().BeFalse();

当然,这既不易于阅读,也不提供 FA 通常质量的诊断输出。是否有任何 FluentAssertions 内置方法来进行此断言?

【问题讨论】:

【参考方案1】:

改进断言的一种方法是将比较提取到自定义IEquivalencyStep 中,以指导如何比较CustomerEmployee

它由两部分组成:

CanHandle 确定此比较何时适用,并且 Handle 执行自定义比较。
public class CustomerEmployeeComparer : IEquivalencyStep

    public bool CanHandle(IEquivalencyValidationContext context,
        IEquivalencyAssertionOptions config)
    
        return context.Subject is Customer
            && context.Expectation is Employee;
    

    public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator
        parent, IEquivalencyAssertionOptions config)
    
        var customer = (Customer)context.Subject;
        var employee = (Employee)context.Expectation;

        customer.Name.Should().Be(employee.FirstName, context.Because, context.BecauseArgs);
        customer.Age.Should().Be(employee.Age, context.Because, context.BecauseArgs);

        return true;
    

要在断言中使用CustomerEmployeeComparer,请通过在BeEquivalentToEquivalencyAssertionOptions config 参数上调用Using(new CustomerEmployeeComparer()) 来添加它。

注意:由于您的示例需要按顺序比较两个列表,因此我在下面的示例中添加了WithStrictOrdering()

[TestMethod]
public void CompareCustomersAndEmployeesWithCustomEquivalencyStep()

    // Arrange
    var customers = new[] 
        new Customer  Name = "John", Age = 42 ,
        new Customer  Name = "Mary", Age = 43 
    ;

    var employees = new[] 
        new Employee  FirstName = "John", Age = 42 ,
        new Employee  FirstName = "Mary", Age = 43 
    ;

    // Act / Assert
    customers.Should().BeEquivalentTo(employees, opt => opt
        .Using(new CustomerEmployeeComparer())
        .WithStrictOrdering());


public class Employee

    public string FirstName  get; set; 
    public int Age  get; set; 


public class Customer

    public string Name  get; set; 
    public int Age  get; set; 


将第一个 Employee 的名称更改为 Jonathan,现在给出以下失败消息:

Message: Expected item[0] to be "Jonathan" with a length of 8, but "John" has a length of 4, differs near "hn" (index 2).

With configuration:
- Use declared types and members
- Compare enums by value
- Include all non-private properties
- Include all non-private fields
- Match member by name (or throw)
- Without automatic conversion.
- UnitTestProject15.CustomerEmployeeComparer
- Without automatic conversion.
- Always be strict about the collection order

对于任何感兴趣的人,有一个相关的未解决问题是关于覆盖要比较的属性。 https://github.com/fluentassertions/fluentassertions/issues/535

【讨论】:

以上是关于FluentAssertions:如何在每对元素上使用自定义比较来比较两个集合?的主要内容,如果未能解决你的问题,请参考以下文章

FluentAssertions:如何指定该集合应包含一定数量的匹配谓词的元素?

FluentAssertions:如何指定该集合应包含一定数量的匹配谓词的元素?

上采样:在向量的每个连续元素之间插入额外的值

FluentAssertions:排序列表的等价性

在 Swift 中,当每对元素都是字符串时,如何遍历一个数组以获取 2 个变量?和一个字符串

如何使用 FluentAssertions 检查对象是不是从另一个类继承?