我们编写了一个如下所示的测试。此测试要求我们为Equal
- 类创建了en 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
- 函数有一个不幸的依赖,这意味着如果我用另外一个字段扩展CodeTableItem
- 类,并忘记扩展Equals
-function,单元测试仍然运行为绿色,但我们不测试所有字段。我们希望避免这种Equality
污染(见Test Specific 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
不相等。我编写了多次运行反射的代码,并且通常最终实现了忽略字段的重载。有没有办法忽略OfLikeness
或ShouldEqual
的某些字段?还是有其他方法可以解决这个问题吗?
答案 0 :(得分:12)
为什么你不想这样做
我不认为从任何List<T>
创建一个相似性可以做你想做的事。据我了解,您想比较两个列表的内容。这与比较两个列表不一样......
考虑Likeness的作用:它比较 property 值。 List<T>
的哪些属性?
他们是
正如Nikos Baxevanis在他的回答中指出的那样,你可以使用Without方法忽略Capacity属性的值,但这意味着只剩下Count属性。
换句话说,如果你这样做了,那就:
expectedValutaListWithLikeness.ShouldEqual(actual.ToList());
在功能上与此相同:
Assert.AreEqual(expected.Count, actual.Count)
换句话说,列表可能具有完全不同的数据,但如果只有每个列表具有相同数量的元素,则测试仍将通过。这可能不是你想要的......
你应该做什么
您可以使用“相似性”将每个元素相互比较。这样的事情应该有效:
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以来已经这么多年了,我记不起那种方法的怪癖......
答案 1 :(得分: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)。
您可以找到有关此功能的更多信息here。
答案 2 :(得分:3)
以下答案来自me asking myself a duplicate of this question,见下文
您可以使用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>
Test Specific Equality的最简洁方法。
OOTB(我已经失去了对我学到的内容的参考),Ploeh.SemanticComparison
Likeness
版本高达2.12仅适用于单个项目。
将相同的技术应用于项目集合的最佳方法是什么(理想情况下是OOTB,但对于经过充分判断的扩展方法套件非常开放),以便于以可组合的方式表达包含嵌入对象的项目的相似性?
这真的是一个自我回答,所以我可以存储帮助者,并允许一个地方放置“你在Vnn这样做”应该相似提供序列支持将来,但它不会这是我第一次对AFflicted AFicionados的回答微妙感到惊讶
答案 3 :(得分: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
));
在此示例中,您首先在Calendar对象上设置要排除的属性等。然后,您提供自定义EqualsWith实现来处理Holidays集合。假期=&gt;然后lambda允许您自定义子比较,就像父对象一样。只要你喜欢很多括号,你就可以继续嵌套。
答案 4 :(得分:1)
我不确定这个问题是否仍然相关,但您要找的是https://github.com/jmansar/SemanticComparisonExtensions
您可以使用.WithCollectionInnerLikeness()进行集合比较,它可以为您提供所需的内容。