14

我们编写了一个如下所示的测试。此测试要求我们Equal为 -class 创建了 en -overload CodeTableItem

ICollection<CodeTableItem> expectedValutaList = new List<CodeTableItem>();
expectedValutaList.Add(new CodeTableItem("DKK", "DKK"));
expectedValutaList.Add(new CodeTableItem("EUR", "EUR"));
RepoDac target = new RepoDac(); 

var actual = target.GetValutaKd();

CollectionAssert.AreEqual(expectedValutaList.ToList(),actual.ToList());

测试工作正常,但不幸地依赖于Equality-function,这意味着如果我CodeTableItem用一个更多字段扩展 -class,并且忘记扩展Equals-function,单元测试仍然运行绿色,尽管我们没有测试所有字段. 我们想避免这种Equality污染(参见测试特定的平等),它只是为了符合测试而编写的。

我们尝试过使用OfLikeness,并以这种方式重写了测试:

ICollection<CodeTableItem> expectedValutaList = new List<CodeTableItem>();
expectedValutaList.Add(new CodeTableItem("DKK", "DKK"));
expectedValutaList.Add(new CodeTableItem("EUR", "EUR"));
var expectedValutaListWithLikeness = 
          expectedValutaList.AsSource().OfLikeness<List<CodeTableItem>>();

RepoDac target = new RepoDac(); 
ICollection<CodeTableItem> actual;

actual = target.GetValutaKd();

expectedValutaListWithLikeness.ShouldEqual(actual.ToList());

但测试失败,因为Capacity不相等。我编写了多次通过反射运行的代码,并且通常最终实现了忽略字段的重载。有没有办法用OfLikenessor忽略某些字段ShouldEqual?还是有其他方法可以解决这个问题?

4

5 回答 5

12

为什么你不想那样做

我不认为从任何人创建一个像List<T>你想要它做的事情。据我了解,您想比较两个列表的内容。这与比较两个列表不同......

考虑一下 Likeness 的作用:它比较属性值。的属性是List<T>什么?

他们是

  • 容量
  • 数数

正如 Nikos Baxevanis 在他的回答中指出的那样,您可以使用 Without 方法忽略 Capacity 属性的值,但这意味着只保留 Count 属性。

换句话说,如果你这样做了,那么:

expectedValutaListWithLikeness.ShouldEqual(actual.ToList());

在功能上等同于:

Assert.AreEqual(expected.Count, actual.Count)

换句话说,列表可能有完全不同的数据,但如果每个列表的元素数量相同,测试仍然可以通过。这可能不是你想要的...

你应该做什么

您可以使用 Likeness 将每个元素相互比较。像这样的东西应该工作:

var expectedValutaList = new List<CodeTableItem>();
expectedValutaList.Add(new CodeTableItem("DKK", "DKK"));
expectedValutaList.Add(new CodeTableItem("EUR", "EUR"));

var expectedValutaListWithLikeness = from cti in expectedValutaList
                                     select cti
                                         .AsSource()
                                         .OfLikeness<CodeTableItem>();

var target = new RepoDac(); 

var actual = target.GetValutaKd();

Assert.IsTrue(expectedValutaListWithLikeness.Cast<object>().SequenceEqual(
    actual.Cast<object>()));

您也可以使用 CollectionAssert 进行断言,但是自从我上次使用 MSTest 以来已经有很多年了,以至于我不记得该方法的怪癖了...

于 2012-07-30T11:56:15.350 回答
8

只需添加.Without(x => x.Capacity)和 Likeness 实例将Capacity在比较值时忽略该属性。

var expectedValutaListWithLikeness = 
      expectedValutaList.AsSource().OfLikeness<List<CodeTableItem>>()
      .Without(x => x.Capacity);

更新:

正如 Mark Seemann 在他的回答中指出的那样,您可能想要的是将每个元素相互比较。这是一种稍微不同的方式,可让您执行非常灵活的比较。

假设 RepoDac 类返回如下内容:

public class RepoDac
{
    public ICollection<CodeTableItem> GetValutaKd()
    {
        return new[]
        {
            new CodeTableItem("DKK", "DKK"),
            new CodeTableItem("EUR", "EUR")
        };
    }
}

对于 上的每个实例,expectedValutaList您可以创建一个使用 Likeness 覆盖 Equals 的动态代理:

var object1 = new CodeTableItem("DKK", "DKK1")
    .AsSource().OfLikeness<CodeTableItem>()
    .Without(x => x.Property2)
    .CreateProxy();

var object2 = new CodeTableItem("EUR2", "EUR")
    .AsSource().OfLikeness<CodeTableItem>()
    .Without(x => x.Property1)
    .CreateProxy();

请注意 object1 和 object2 如何具有甚至不同的动态生成的 Equals。(第一个忽略 Property2 而第二个忽略 Property1。)

以下测试通过:

var expected = new List<CodeTableItem>();
expected.Add(object1);
expected.Add(object2);

var target = new RepoDac();
var actual = target.GetValutaKd();

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

笔记:

需要从expected包含动态生成的代理(覆盖 Equals)的实例开始。

您可以在此处找到有关此功能的更多信息。

于 2012-07-30T10:07:37.100 回答
3

以下答案来自我问自己这个问题的副本,见下文

您可以使用SequenceLike暗示 LINQ 运算符的SequenceEqual操作。

这允许人们写:-

[Theory, AutoData]
public void ShouldMap(  Dto inputDto )
{
    var mapped = inputDto.ToModel();

    inputDto.AsSource().OfLikeness<Model>()
        .Without( x => x.IgnorableProperty )
        .With( x => x.Tags ).EqualsWhen( ( dto, model ) => 
            model.Tags.SequenceLike( dto.Tags ) )
        .ShouldEqual( mapped );
}

得益于@Nikos Baxevanis 的提示,基于@Mark Seemann 的回答的多合一助手的闪亮简短实现:-

static class LikenessSequenceExtensions
{
    public static bool SequenceLike<T, TSource>( this IEnumerable<T> that, IEnumerable<TSource> source )
    {
        return SequenceLike<T, TSource>( that, source, x => x );
    }

    public static bool SequenceLike<T, TSource>( this IEnumerable<T> that, IEnumerable<TSource> source, Func<Likeness<TSource, T>, IEquatable<T>> customizeLikeness )
    {
        return source.Select( x => customizeLikeness( x.AsSource().OfLikeness<T>() ) ).SequenceEqual( that.Cast<object>() );
    }
}

我的原始实现:

static class LikenessSequenceExtensions0
{
    public static bool SequenceLike0<T, TSource>( this T[] that, TSource[] source )
    {
        return source.SequenceLike0( that, likeness => likeness );
    }

    public static bool SequenceLike0<T, TSource>( this T[] that, TSource[] source, Func<Likeness<TSource, T>, IEquatable<T>> customizeLikeness )
    {
        return source.SequenceEqual( that, ( x, y ) => customizeLikeness( x.AsSource().OfLikeness<T>() ).Equals( y ) );
    }

    public static bool SequenceEqual<T, TSource>( this T[] that, TSource[] source, Func<T, TSource, bool> equals )
    {
        return that.Length == source.Length && that.Zip( source, Tuple.Create ).All( x => equals( x.Item1, x.Item2 ) );
    }
}

原始重复问题

我正在寻找最简洁的方法来管理我的基于 xunit.AutoFixture 的测试中的数组/ /的测试特定相等性。IEnumerable<T>seq<'T>

OOTB(我已经失去了我在哪里学到这个的参考),最高 2.12Ploeh.SemanticComparisonLikeness版本仅适用于单个项目。

将相同技术应用于项目集合(理想情况下是 OOTB,但对经过良好判断的扩展方法套件非常开放)以促进以可组合方式表达包含嵌入对象的项目的相似性的最佳方法是什么?

这确实是一个自我回答,所以如果 Likeness 将来提供序列支持,我可以隐藏助手并允许放置一个“你在 V nn 中这样做”的地方,但这不会是我第一次'我对 AFflicted AFicionados 可能回答的微妙之处感到惊讶

于 2012-10-10T12:21:50.957 回答
3

我想为其他有此问题的人明确说明 - 使用 Ruben 的第二个代码示例,您想在其中比较 Calendar 和 Calendar.Holidays,同时自定义两者的比较:

var expectedCalendar = newCalendar.AsSource()
   .OfLikeness<Calendar>()
   .Without(c=>c.Id) //guid, will never be equal
   .With(c=>c.Holidays).EqualsWhen((source, dest) => 
      source.Holidays.SequenceLike(dest.Holidays, holiday => 
          holiday.Without(h=>h.SecondsUntil) //changes every second
   ));

在此示例中,您首先在日历对象上设置要排除的属性等。然后你给出一个自定义的 EqualsWith 实现来处理 Holidays 集合。然后,holiday=> lambda 允许您自定义子比较,就像父级一样。只要您喜欢大量括号,就可以继续嵌套。

于 2012-12-18T06:01:27.297 回答
1

我不确定这个问题是否仍然相关,但您正在寻找的是https://github.com/jmansar/SemanticComparisonExtensions

您可以使用 .WithCollectionInnerLikeness() 进行集合比较,它会得到您想要的。

于 2017-09-08T18:27:57.223 回答