更新
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 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 Graph Comparison 说:
值类型
确定 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<>)));
不幸的是,不存在这样的覆盖。
a 提出的另一个解决方案是扩展方法。这是一个非常简单的实现:
public static class FluentAssertionsExtensions
{
public static void BeEquivalentByMembers<TExpectation>(
this ComparableTypeAssertions<TExpectation> actual,
TExpectation expectation)
{
actual.BeEquivalentTo(
expectation,
options => options.ComparingByMembers<TExpectation>());
}
}