比较单元测试中的类似对象

时间:2015-07-17 16:47:15

标签: c# unit-testing

比较两个相似物体的最佳方法是什么?

给定FlintlockDTOFlintlock

public class FlintlockDTO
{
  public string GName { get; set; }

  public string SharedPropertyName { get; set; }

  ...
}

public class Flintlock
{
  public Flintlock(FlintlockDTO inflator)
  {
    this.GoodName = inflator.GName;
    this.SharedPropertyName = inflator.SharedPropertyName;
    ...
  }

  public string GoodName { get; private set; }

  public string SharedPropertyName { get; private set; }
  ...
}

如果两个类共享N个属性(例如SharedPropertyName),但M属性相同,但名称不同(例如GoodName \ GName。)

fluentassert之类的工具差不多这样做,如果属性名称匹配,根据我的理解,这样可行:

flintlockDto.ShouldBeEquivalentTo(flintlock);

有没有办法在fluentassert或任何其他工具中巧妙地做到这一点?

理想地,

flintlockDto.IsTheSameAs(flintlock).WhenMapping("GName","GoodName");

4 个答案:

答案 0 :(得分:2)

当我想要比较特定属性时,我有时会使用的一种策略是利用匿名类型,如下所示:

Assert.AreEqual(
    new{flintlockDto.GoodName, flintlockDto.SharedPropertyName},
    new{GoodName = flintlock.GName, flintlock.SharedPropertyName});

这并不依赖于任何特定的测试框架。它利用自动生成的Equals()方法用于匿名类型,并且在失败的情况下,自动生成的ToString()方法可以让您完整地描述两个对象的外观,这使得弄清楚出了什么问题。

您可能还想查看Mark Seemann的Likeness类型:

  

如何在不引入平等污染的情况下解决这个难题? AutoFixture以通用Likeness类的形式提供一个选项。此类提供从TSource到TDestination的基于约定的特定于测试的相等映射,并覆盖Equals方法。

     

...可以自定义比较以覆盖某些属性的行为......

答案 1 :(得分:2)

我决定详细说明StriplingWarrior提到的Likeness。它以nuget package提供。

以下是一个例子:

using NUnit.Framework;
using Ploeh.SemanticComparison;
using Ploeh.SemanticComparison.Fluent;

namespace Tests
{
    [TestFixture]
    class Tests2
    {
        [Test]
        public void ObjectsShuldEqual()
        {
            var flintlockDto = new FlintlockDTO()
            {
                GName = "name",
                AdditionalProperty = "whatever",
                SharedPropertyName = "prop name"
            };
            var flintlock = new Flintlock(flintlockDto);

            Likeness<Flintlock, FlintlockDTO> flintFlockDtoLikeness = flintlock
                .AsSource().OfLikeness<FlintlockDTO>()
                .With(dto => dto.GName).EqualsWhen((flintlock1, dto) => flintlock1.GoodName == dto.GName) // you can write an extension method to encapsulate it
                .Without(dto => dto.AdditionalProperty);

            // assert
            flintFlockDtoLikeness.ShouldEqual(flintlockDto);
        }
    }

    public class FlintlockDTO
    {
        public string GName { get; set; }

        public string SharedPropertyName { get; set; }

        public string AdditionalProperty { get; set; }
    }

    public class Flintlock
    {
        public Flintlock(FlintlockDTO inflator)
        {
            this.GoodName = inflator.GName;
            this.SharedPropertyName = inflator.SharedPropertyName;
        }

        public string GoodName { get; private set; }

        public string SharedPropertyName { get; private set; }
    }
}

如你所见:

  • 它对具有相同名称的属性执行自动比较
  • 您可以指定不同名称的属性是否应该匹配(这个实际上非常难看,但您可以编写一个扩展方法来封装它)。
  • 您可以指定是否不应比较某个属性

答案 2 :(得分:1)

您可以在测试项目中编写扩展类/方法并获得所需的结果,并且不会使用不必要的逻辑污染生产代码库。

static class Extensions
{
  public static bool IsEqualTo(this FlintlockDTO expected, Flintlock actual) 
  {
    return expected.GName == actual.GoodName && expected.SharedPropertyName == actual.SharedPropertyName;
  }
}

在你的测试中,你将能够运行它:

Assert.IsTrue(expected.IsEqualTo(actual));

并且测试逻辑将无法在您的生产代码库中使用。

答案 3 :(得分:0)

FlintlockDTO实施IEquatable<Flintlock>如下所示:

public override Equals(Flintlock other)
{
    return GName == other.GoodName; // modify as necessary (case, culture, etc.)
}

(并且不要忘记覆盖并正确实施GetHashCode。)

然后,您可以检查是否

flintlockDto.Equals(flintlock)

在您选择的单元测试框架中。