问题描述
我正在使用名为Optional
(https://github.com/nlkl/Optional)的库,该库允许功能语言通用的“也许”抽象。
该库很棒,但是我面临有关测试的问题:我无法正确测试两个可选实例是否等效。
为了测试是否等效,我使用了Fluent Assertions。但是,我没有得到想要的结果。
我将用代码说明问题:
#load "xunit"
[Fact]
void TestOptional()
{
var a = new[] { 1,2,3 }.some();
var b = new[] { 1,3 }.some();
a.Should().BeEquivalentTo(b);
}
如屏幕截图所示,该测试失败(为方便起见,我使用LINQPad)
如您所见,这不是人们期望的。
如何告诉Fluent断言使用Option类型正确检查等效性?
解决方法
更新
I opened an issue on Github关于您的问题,昨天是pull request was merged,因此下一(预)发行版应使您能够优雅地解决问题:
新的重载使您可以使用开放的泛型类型。 如果同时指定了开放式和封闭式,则封闭式优先。
SelfReferenceEquivalencyAssertionOptions
添加了以下方法:
-
public TSelf ComparingByMembers(System.Type type) { }
-
public TSelf ComparingByValue(System.Type type) { }
Here's the unit test已添加到Fluent断言中,以显示其工作原理:
[Fact]
public void When_comparing_an_open_type_by_members_it_should_succeed()
{
// Arrange
var subject = new Option<int[]>(new[] { 1,3,2 });
var expected = new Option<int[]>(new[] { 1,2,3 });
// Act
Action act = () => subject.Should().BeEquivalentTo(expected,opt => opt
.ComparingByMembers(typeof(Option<>)));
// Assert
act.Should().NotThrow();
}
Fluent Assertions - Object Graph Comparison说:
值类型
确定是否应将Fluent断言重复出现在对象的 属性或字段,它需要了解哪些类型具有价值 语义以及应将哪些类型视为引用类型。的 默认行为是对待覆盖Object.Equals的每种类型 作为旨在具有值语义的对象。不幸, 匿名类型和元组也重写此方法,但是因为我们 倾向于在等效性比较中经常使用它们,我们总是 比较它们的属性。
您可以使用
ComparingByValue<T>
或 各个断言的ComparingByMembers<T>
选项
Option<T>
是struct
,并且覆盖Equals
,因此Fluent断言将a
和b
与值语义进行比较。
Option<T>
这样实现Equals
:
public bool Equals(Option<T> other)
{
if (!this.hasValue && !other.hasValue)
return true;
return this.hasValue
&& other.hasValue
&& EqualityComparer<T>.Default.Equals(this.value,other.value);
}
因此,int[]
通过引用进行比较,您的测试失败。
您可以分别为每个测试覆盖此行为,例如Guro Stron说:
a.Should().BeEquivalentTo(b,opt => opt.ComparingByMembers<Option<int[]>>());
或通过静态AssertionOptions
类在全局范围内:
AssertionOptions.AssertEquivalencyUsing(options =>
options.ComparingByMembers<Option<int[]>>());
编辑:
对于您的情况,Fluent断言将需要一个AssertEquivalencyUsing
覆盖来支持未绑定的泛型类型:
AssertionOptions.AssertEquivalencyUsing(options =>
options.ComparingByMembers(typeof(Option<>)));
不幸的是,不存在这样的替代。
提出的另一种解决方案是扩展方法。这里是一个非常简单的实现:
public static class FluentAssertionsExtensions
{
public static void BeEquivalentByMembers<TExpectation>(
this ComparableTypeAssertions<TExpectation> actual,TExpectation expectation)
{
actual.BeEquivalentTo(
expectation,options => options.ComparingByMembers<TExpectation>());
}
}