使用SemanticComparison比较嵌套对象属性

时间:2014-01-09 14:17:13

标签: c# .net unit-testing semantic-comparison

我正在创建单元测试,需要成员比较两个相同类型的对象。我决定使用SemanticComparison库来处理此任务,而无需编写自定义比较器代码。它在比较平面对象时效果很好,当对象包含也需要成员比较的嵌套对象时会出现问题。

public class Outer
{
    public string Name { get; set; }
    public Inner Inner { get; set; }
}

public class Inner
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public class Service
{
    public Outer Method()
    {
        return new Outer()
        {
            Name = "outerName",
            Inner = new Inner()
            {
                Name = "innerName",
                Value = "value1"
            }
        };
    }
}

这不起作用,因为Inner对象是通过引用进行比较,而非成员进行比较:

    [Test]
    public void SimpleTest1()
    {

        // setup
        var expectedLikeness = new Outer()
        {
            Name = "outerName",
            Inner = new Inner()
            {
                Name = "innerName",
                Value = "value1"
            }
        }.AsSource().OfLikeness<Outer>();

        var sut = new Service();
        // exercise sut
        var actual = sut.Method();
        // verify
        expectedLikeness.ShouldEqual(actual);
    }

为了使它工作,我必须创建嵌套对象的代理,以便它覆盖默认的equals实现。

    [Test]
    public void SimpleTest2()
    {

        // setup
        var expectedLikeness = new Outer()
        {
            Name = "outerName",
            Inner = new Inner()
            {
                Name = "innerName",
                Value = "value1"
            }.AsSource().OfLikeness<Inner>().CreateProxy()
        }.AsSource().OfLikeness<Outer>();

        var sut = new Service();
        // exercise sut
        var actual = sut.Method();
        // verify
        expectedLikeness.ShouldEqual(actual);
    }

嗯,它运行正常,但想象一下,在一些服务代码重构后,我们引入了导致Inner类的value属性与预期值不同的bug。 SemanticComparison的一个很酷的特性是它可以记录导致不平等的成员的名称。但是,在这种情况下,它只会返回“Inner”作为不匹配,而不是Inner类中特定属性的名称。

我错过了什么吗?是否可以将其配置为能够返回实际的不匹配成员。

对于简单的数据结构来说,这显然不是一个问题,就像在这个例子中一样,但它可能会给测试现实代码带来不便。

2 个答案:

答案 0 :(得分:3)

由于没有人回答,我会提供自己的答案。

所以,看起来你不能这样做OOTB,除非你写一些额外的代码。 我已将代码包装在一组扩展方法中。这些方法允许您指定应使用内部相似性而不是引用来比较内部属性/集合属性。您无需手动创建任何代理,所有内容都由这些扩展内部处理。并记录所有内部比较的结果,因此您可以确切地看到哪个成员具有无效值。

以下是使用“WithInnerLikeness”扩展方法的问题的测试。

    [Test]
    public void ServiceTest3()
    {
        // setup
        var expected = new Outer()
        {
            Name = "outerName",
            Inner = new Inner()
            {
                Name = "innerName",
                Value = "value2"
            }
        };

        var expectedLikeness = expected.AsSource().OfLikeness<Outer>()
            .WithInnerLikeness(d => d.Inner, s => s.Inner)
            ;

        var sut = new Service();
        // exercise sut
        var actual = sut.Method();
        // verify
        expectedLikeness.ShouldEqual(actual);
    }

您可以看到内部对象的值属性不匹配,因此测试应该失败。它在输出中失败并显示以下消息:

Comparing inner properties using likeness. Source: s => s.Inner Destination: d => d.Inner.
The source and destination values are not equal. Details: The provided value ClassLibrary1.Inner did not match the expected value ClassLibrary1.Inner. The following members did not match:
- Value.

Ploeh.SemanticComparison.LikenessException : The provided value ClassLibrary1.Outer did not match the expected value ClassLibrary1.Outer. The following members did not match:
- Inner.

您可以在github上找到源代码和更多示例。

https://github.com/jmansar/SemanticComparisonExtensions

答案 1 :(得分:0)

这真是可惜,不开箱即用。

您也可以尝试here,请参见Fluent Assertions

actual.Should().BeEquivalentTo(expected);

在比较,成员排除等方面有很多灵活性,并且开箱即用地支持嵌套对象属性。

希望这会有所帮助!