流利的断言;结合集合和对象图比较断言

Posted

技术标签:

【中文标题】流利的断言;结合集合和对象图比较断言【英文标题】:FluentAssertions; combining collection and object graph comparison assertions 【发布时间】:2016-10-19 10:32:09 【问题描述】:

我正在尝试使用 FluentAssertions 来组合集合和对象图比较断言。

我有以下课程。

public class Contract

    public Guid Id  get; set; 
    public string Name  get; set; 

在集合中返回,就像这样。

ICollection<Contract> contracts = factory.BuildContracts();

然后我想确保该集合仅包含特定的 Contract 对象。

contracts.Should().Contain(new Contract()  Id = id1, Name = "A" );

这不起作用,我相信因为Contain 使用的是object.Equals 而不是对象图比较(由ShouldBeEquivalentTo 提供)。

我还需要断言该集合不包含特定对象,即

contracts.Should().NotContain(new Contract()  Id = id2, Name = "B" );

有效地给定一个包含未知数量项目的集合,我想确保;它包含许多特定项目,并且它不包含许多特定项目。

这可以使用 FluentAssertions 提供的函数来实现吗?

附带说明,出于此处讨论的原因,我不想覆盖object.Equals。 Should I be using IEquatable to ease testing of factories?

【问题讨论】:

【参考方案1】:

据我从文档和我使用框架的经验来看,它确实使用了object.Equals

在这种情况下,我倾向于使用the collections documentation for v3.0 and higher 中引用的表达式谓词。

以下示例显示如何确保集合仅包含特定的 Contract 对象,并断言该集合不包含特定对象。

[TestMethod]
public void FluentAssertions_Should_Validate_Collections() 
    //Arrange
    var id1 = Guid.NewGuid();
    var id2 = Guid.NewGuid();

    var list = new List<Contract>
        new Contract()  Id = id1, Name = "A" ,
        new Contract()  Id = Guid.NewGuid(), Name = "B"
    ;

    var factoryMock = new Mock<IContractFactory>();
    factoryMock.Setup(m => m.BuildContracts()).Returns(list);
    var factory = factoryMock.Object;

    //Act
    var contracts = factory.BuildContracts();

    //Assert
    contracts.Should()
        .HaveCount(list.Count)
        .And.Contain(c => c.Id == id1 && c.Name == "A")
        .And.NotContain(c => c.Id == id2 && c.Name == "B");

【讨论】:

【参考方案2】:

您可以覆盖您的合同Equals,然后将使用该合同,您的单元测试应该会顺利通过。 如果您使用的是随机 Guid,这可能是个问题,但您似乎使用的是预定义的。

尝试以下方法:

protected bool Equals(Contract other)

    return Id.Equals(other.Id) && string.Equals(Name, other.Name);


public override bool Equals(object obj)

    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    if (obj.GetType() != this.GetType()) return false;
    return Equals((Contract) obj);


public override int GetHashCode()

    unchecked
    
        return (Id.GetHashCode()*397) ^ (Name != null ? Name.GetHashCode() : 0);
    

如您所见,它通过了测试:

【讨论】:

+1 并感谢您的建议。然而,这并不是我真正想要的答案,我曾经重写 equals 并只使用标准的Assert.IsTrue(contracts.Contains(...))。但这有其自身的问题(***.com/questions/33988487/…)。我希望使用 FluentAssertions 来避免覆盖 Equals。 有趣的链接...我相信你会想出办法的。我会密切关注这个帖子,看看它是如何演变的:)。顺便说一句,你给“shouldly”跑了吗?我不确定它的默认行为是什么,但它可能会按照你想要的方式工作。【参考方案3】:

除了Nkosi's 答案,您仍然可以通过构建期望来使用ShouldBeEquivalentTo

【讨论】:

你的意思是像Should().Contain(c =&gt; c.ShouldBeEquivalentTo(contract1))吗?不知道如何让它工作,因为ShouldBeEquivalentTo 没有返回值。不完全是您所说的“建立期望”。谢谢 只需按照您的期望使用Contract 实例创建一个集合,并将其作为期望传递给ShouldBeEquivalentTo。如果您关心严格的顺序,您甚至可以使用options =&gt; options.WithStrictOrdering' 如果集合中包含其他我不关心的Contracts怎么办? IE。该集合包含 100 个项目,我想确保它包含 2 个特定项目,并且不包含 1 个特定项目。

以上是关于流利的断言;结合集合和对象图比较断言的主要内容,如果未能解决你的问题,请参考以下文章

如何使用流利的断言结合集合和属性断言?

如何使用流利的断言比较列表?

流利的断言:大约比较两个数字集合

流利的断言应该AllBeEquivalentTo

如何使用流利的断言报告对象的名称

如何使用流利断言断言集合中的所有项目?