【问题标题】:FluentAssertions: How to compare two collections using a custom comparison on each pair of elements?FluentAssertions:如何在每对元素上使用自定义比较来比较两个集合?
【发布时间】: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 内置方法来进行此断言?

【问题讨论】:

    标签: c# unit-testing xunit assertion fluent-assertions


    【解决方案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

    【讨论】:

      猜你喜欢
      • 2023-03-23
      • 2019-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-10
      相关资源
      最近更新 更多