为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
慕少森 2023-08-20 10:23:35
我有一个利用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" },    ...}我将它们进行比较如下:// ACTvar result = systemUnderTest.GetNames();// ASSERTexpected.ToExpectedObject().ShouldMatch(result);这将会失败。被测对象实际返回的集合是和GetNames的混合集合,并且没有一个会匹配,因为返回对象的属性非零,而我不关心的其他属性甚至不存在于该集。SimpleEntitiesComplexEntitiesIdComplexEntityexpectedExpectedObjects 文档没有给我这方面的指导。ShouldMatch应该适用于匿名类型,所以这应该(并且确实)有效:expected.Select(e => new { e.Name }).ToExpectedObject().ShouldMatch(result);但我认为这是一种不灵活的解决方法。还有许多其他带有契约的方法,仅包括我不关心底层类型的接口。我必须在每个此类测试中从接口手动选择相同的属性,并且如果该接口发生更改以包含更多属性,则测试仍然会通过,即使它们无法正确检查契约。例如,如果我将Id属性添加到INamed,测试仍然会通过,甚至永远不会测试Id,从而允许错误默默地继续。是否有一种开箱即用的方法可以使 ExpectedObjects 仅比较公共接口?我想我可以手动编写一个人为的动态方法,从接口中创建一个匿名类型,使其通用并使用它,其用法如下:expected.ToExpectedObject().ShouldMatchInterface<INamed>(result);但这首先让我对使用该库产生疑问,因为投入的努力ShouldMatchInterface可能会很大。
查看完整描述

1 回答

?
慕哥9229398

TA贡献1877条经验 获得超6个赞

您似乎没有正确设置预期对象。


例如,使用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因为您不能只创建一个新的抽象类或接口。


查看完整回答
反对 回复 2023-08-20
  • 1 回答
  • 0 关注
  • 123 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信