繁体   English   中英

ExpectedObjects 比较具体的对象,而不仅仅是接口

[英]ExpectedObjects compares concrete objects instead of only interfaces

我有一个使用ExpectedObjects的 xUnit 单元测试项目。 现在我正在尝试测试一个返回符合接口的对象的服务。 为了这个问题,让我们举一个例子界面

public interface INamed
{
    string Name { get; }
}

和两种不同的实现:

public class ComplexEntity : INamed
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Domain { get; set; }

    /* Many other properties follow... */
}
public class SimpleEntity : INamed
{
    public int Id { get; set; }
    public string Name { get; set; }
}

被测方法的签名为

public IEnumerable<INamed> GetNames();

我关心的只是返回正确的名称。 因此,在模拟服务可能需要的任何依赖项之后,我构建了一个预期结果集,如下所示:

IEnumerable<INamed> expected = new []
{
    new SimpleEntity { Name = "NAM1" },
    new SimpleEntity { Name = "NAM2" },
    ...
}

我将它们进行了如下比较:

// ACT
var result = systemUnderTest.GetNames();

// ASSERT
expected.ToExpectedObject().ShouldMatch(result);

这将失败。 被测GetNames实际返回的集合是SimpleEntitiesComplexEntities的混合集合,没有一个匹配,因为返回对象的Id属性是非零的,而我不关心的ComplexEntity其他属性甚至不存在于expected集合中。

ExpectedObjects 文档对此没有提供任何指导。 ShouldMatch应该在匿名类型上工作,所以这应该(并且确实)工作:

expected.Select(e => new { e.Name }).ToExpectedObject().ShouldMatch(result);

但我认为这是一种不灵活的解决方法。 还有许多其他带有契约的方法,仅包括我不关心底层类型的接口。 在每个此类测试中,我都必须从接口中手动选择相同的属性,如果该接口曾经更改为包含更多属性,则即使它们无法正确检查合同,测试仍将通过 例如,如果我将Id属性添加到INamed ,测试仍然会通过,甚至不会测试Id ,允许错误继续进行。

是否有一种开箱即用的方法可以让 ExpectedObjects 仅比较公共接口? 我想我可以手动编写一个人为的动态方法,从接口创建一个匿名类型,使其通用并使用它,用法如下:

expected.ToExpectedObject().ShouldMatchInterface<INamed>(result);

但这首先让我质疑使用该库,因为在ShouldMatchInterface付出的努力可能会很重要。

看起来您没有正确设置预期对象。

例如,使用这样的GetNames()实现:

public class Foo
{
    public IEnumerable<INamed> GetNames()
    {
        return new[] {
            new ComplexEntity() { Id = 1, Name = "NAM1", Domain = "DOM1" },
            new ComplexEntity() { Id = 2, Name = "NAM2", Domain = "DOM2" }
        };
    }
}

以下 xUnit + ExpectedObjects 测试将通过:

using ConsoleProject;
using ExpectedObjects;
using Xunit;

namespace ConsoleProject_Tests
{
    public class ExpectedObjectsTests
    {
        [Fact]
        public void GetNames_should_return_INamed_ExpectedObjects_Style()
        {
            var expected = new[]
            {
                new { Name = "NAM1" },
                new { Name = "NAM2" }
            }.ToExpectedObject();

            var systemUnderTest = new Foo();
            var actual = systemUnderTest.GetNames();

            expected.ShouldMatch(actual);
        }
    }
}

请注意, ToExpectedObject()是匿名对象,而不是具体类。

现在将它与通过实现自定义IEqualityComparer<INamed>的旧方法进行比较,该方法恰好在测试类中...

using ConsoleProject;
using System;
using System.Collections.Generic;
using Xunit;

namespace ConsoleProject_Tests
{
    public class ClassicXUnitTests : IEqualityComparer<INamed>
    {
        bool IEqualityComparer<INamed>.Equals(INamed x, INamed y)
        {
            if (x == null && y == null) return true;
            if (x == null || y == null) return false;
            return String.Equals(x.Name, y.Name);
        }

        int IEqualityComparer<INamed>.GetHashCode(INamed obj)
        {
            return obj.Name.GetHashCode();
        }

        [Fact]
        public void GetNames_should_return_INamed_xUnit_Style()
        {
            var expected = new[]
            {
                new SimpleEntity() { Name = "NAM1" },
                new SimpleEntity() { Name = "NAM2" }
            };

            var systemUnderTest = new Foo();
            var actual = systemUnderTest.GetNames();

            Assert.Equal<INamed>(expected, actual, this);
        }
    }
}

它仍然需要一个实现INamed的具体类,因为您不能只创建一个新的抽象类或接口。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM