[英]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
实际返回的集合是SimpleEntities
和ComplexEntities
的混合集合,没有一个匹配,因为返回对象的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.