[英]Testing Optional equivalence with FluentAssertions
我正在使用一个名为Optional
( https://github.com/nlkl/Optional ) 的库,它允许函数式语言常见的“可能”抽象。
该库很棒,但我在测试方面面临一个问题:我无法正确测试 2 个可选实例是否等效。
为了测试等效性,我使用Fluent Assertions 。 但是,我没有得到想要的结果。
我将用代码说明问题:
#load "xunit"
[Fact]
void TestOptional()
{
var a = new[] { 1, 2, 3 }.Some();
var b = new[] { 1, 2, 3 }.Some();
a.Should().BeEquivalentTo(b);
}
这个测试失败了,正如我在屏幕截图中显示的(为了方便,我正在使用 LINQPad)
如您所见,这不是人们所期望的。
我如何告诉 Fluent Assertions 使用 Option 类型正确检查等效性?
我在 Github 上打开了一个关于你的问题的问题,昨天 合并了一个 拉取请求,所以下一个(预)发布应该能让你优雅地解决你的问题:
新的重载允许您使用开放的泛型类型。 如果同时指定了开放类型和封闭类型,则封闭类型优先。
SelfReferenceEquivalencyAssertionOptions
添加了以下方法:
public TSelf ComparingByMembers(System.Type type) { }
public TSelf ComparingByValue(System.Type type) { }
这是添加到 Fluent Assertions 的单元测试,展示了它是如何工作的:
[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.Equals 的每个类型视为旨在具有值语义的对象。 不幸的是,匿名类型和元组也覆盖了这个方法,但是因为我们倾向于在等价比较中经常使用它们,所以我们总是通过它们的属性来比较它们。
您可以通过为各个断言使用
ComparingByValue<T>
或ComparingByMembers<T>
选项轻松覆盖它
Option<T>
是一个struct
并覆盖Equals
,因此 Fluent Assertions 将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 Assertions 需要一个支持未绑定泛型类型的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>());
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.