如何在 FluentAssertions 中使用方法

Posted

技术标签:

【中文标题】如何在 FluentAssertions 中使用方法【英文标题】:How to use the method When in FluentAssertions 【发布时间】:2020-09-16 01:32:09 【问题描述】:

有时我必须断言两个列表具有相同的项目。使用流利的断言可以这样做:

class MyObject  public string MyString get; set; 

var o1 = new MyObject  MyString = "1    " 
list1.Add(o1);

var o2 = new MyObject  MyString = "1" 
list2.Add(o2);

list1.Should().BeEquivalentTo(list2)

但有时我希望以不同的方式比较特定属性,例如:

list1.Should().BeEquivalentTo(list2, options => options
            .Using<string>(context => context.Subject.TrimEnd().Should().Be(context.Expectation))
            .When<string>( ??????? ));

我试过了:

it => it.SelectedMemberInfo.Name == PropertyNameHere

但是 SelectedMemberInfo 可以为 null 并且当为 null 时会引发异常,我不知道我是否以正确的方式调用它。

更新 1:已尝试,但为空引用异常:

        options => options
        .Using<DateTime>(it => it.Subject.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMinutes(1)))
        .When(it =>
            it != null
            && it.SelectedMemberInfo != null
            && it.SelectedMemberInfo.Name == nameof(Y.X)));

【问题讨论】:

你能提供一个Minimal Complete Example 你想要达到的目标吗? 您能具体说明我的问题中缺少什么吗?我觉得很容易理解 在这种情况下,我不清楚 list1list2 是。如果您提供完整的示例,我可以将其复制/粘贴到 IDE 中并运行。 已编辑问题 【参考方案1】:

要匹配MyString 属性,最常用的两种方法是按类型匹配或按路径匹配。

要按类型匹配MyString,请使用WhenTypeIs&lt;string&gt;

[TestMethod]
public void MatchByType()

    var o1 = new MyObject  MyString = "1    " ;
    var list1 = new[]  o1 ;

    var o2 = new MyObject  MyString = "1" ;
    var list2 = new[]  o2 ;

    list1.Should().BeEquivalentTo(list2, opt => opt
        .Using<string>(ctx => ctx.Subject.TrimEnd().Should().Be(ctx.Expectation))
        .WhenTypeIs<string>());

对于好奇的WhenTypeIs&lt;TMemberType&gt;只是一个别名

When(info => info.RuntimeType.IsSameOrInherits(typeof(TMemberType))

要通过名称匹配MyString,您可以使用When(e =&gt; e.SelectedMemberPath)

SelectedMemberPath 定义为

从根对象到当前对象的完整路径,用点分隔。

在这种情况下,根对象是MyObject,所以SelectedMemberPath 将是"MyString"。 所以可以写成:

[TestMethod]
public void MatchByName()

    var o1 = new MyObject  MyString = "1    " ;
    var list1 = new[]  o1 ;

    var o2 = new MyObject  MyString = "1" ;
    var list2 = new[]  o2 ;

    list1.Should().BeEquivalentTo(list2, opt => opt
        .Using<string>(ctx => ctx.Subject.TrimEnd().Should().Be(ctx.Expectation))
        .When(e => e.SelectedMemberPath.EndsWith(nameof(MyObject.MyString))));

【讨论】:

【参考方案2】:

这里的问题是您试图访问尚未实例化的对象中的成员。因为it.SelectedMemberInfonull,所以没有成员Name 可以访问。 NullReferenceException 上的 .NET 文档对此有很好的解释。

https://docs.microsoft.com/en-us/dotnet/api/system.nullreferenceexception?view=netcore-3.1

假设SelectedMemberInfo 为空时没有其他特殊情况,您只想使.When() 中的表达式为空安全。您可以使用单个字符轻松完成此操作(空传播):

it => it.SelectedMemberInfo?.Name == PropertyNameHere

null 传播的工作方式是,如果问号前面的对象为 null,它将在语句末尾评估为该类型的 null 实例。所以在这种情况下,如果it.SelectedMemberInfo 为空,则计算结果为(string) null

如果你喜欢额外的线条,你也可以这样做 -

it => it.SelectedMemberInfo != null && it.SelectedMemberInfo.Name == PropertyNameHere)

如果您有可能在父集合中有 null 项,您还需要对其进行 null 检查 -

it => it?.SelectedMemberInfo?.Name == PropertyNameHere

【讨论】:

更新了问题并尝试了您的建议,但仍然为空参考

以上是关于如何在 FluentAssertions 中使用方法的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

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

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