如何使用 FluentAssertions 控制字典成员的 "Equality"
How to control "Equality" for dictionary members using FluentAssertions
有没有办法通过 FluentAssertions 来控制如何比较字典的值是否相等?
我有一本 class,其中一本 属性 是字典 (string/double)。我想比较 class 的两个实例(预期的和实际的),并为字典成员指定如何确定 "equality"。
假设我有一个 class,如图所示:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var t1 = new Thing();
t1.Name = "Bob";
t1.Values.Add("Hello", 100.111);
t1.Values.Add("There", 100.112);
t1.Values.Add("World", 100.113);
var t2 = new Thing();
t2.Name = "Bob";
t2.Values.Add("Hello", 100.111);
t2.Values.Add("There", 100.112);
t2.Values.Add("World", 100.1133);
t1.Should().BeEquivalentTo(t2);
}
}
public class Thing
{
public string Name { get; set; }
public Dictionary<string, double> Values { get; set; } = new Dictionary<string, double>();
}
我希望能够指定如何比较字典中的 "World" 条目。实际上,这些值可能会非常大,或者在小数点后 10 位以上(但之后不会)相同,但我想我可能需要说“如果差异小于 1% 则相同”。
我喜欢 FluentAssertions 告诉我成员及其不同原因的方式,并尝试了自定义 IAssertionRule(使用 Options lambda),但这似乎只比较了 class 属性,而不是字典的成员。
我不拥有被比较的 classes,因此无法覆盖 "Equal" 方法,而且我找不到指定自定义比较器 (IEquatable) 的方法 - 但我怀疑我会失去关于为什么它们不一样的流畅细节。
如果可能,但任何方法也适用于作为 Class 属性的双精度数(与字典中的值相反),那没问题。
谢谢。
BeApproximately
可用于在可接受的精度范围内比较双打。将它与配置所有双打精度一起使用应该满足所需的行为。
t1.Should().BeEquivalentTo(t2, options => options
.Using<double>(ctx =>
ctx.Subject.Should().BeApproximately(ctx.Expectation, ctx.Expectation * 0.01D))
.WhenTypeIs<double>()
);
根据 Nkosi 的回答,这是我正在使用的 BeApproximately 的一个示例(允许将 BeApproximately
与 decimal?
一起使用):
[CustomAssertion]
public static void BeApproximately(this NullableNumericAssertions<decimal> value, decimal? expected, decimal precision, string because = "",
params object[] becauseArgs)
{
if (expected == null)
value.BeNull(because);
else
{
if (!Execute.Assertion.ForCondition(value.Subject != null).BecauseOf(because)
.FailWith($"Expected {{context:subject}} to be '{expected}' {{reason}} but found null"))
return;
Decimal num = Math.Abs(expected.Value - (Decimal) value.Subject);
Execute.Assertion.ForCondition(num <= precision).BecauseOf(because, becauseArgs).FailWith("Expected {context:value} to approximate {1} +/- {2}{reason}, but {0} differed by {3}.", (object) value.Subject, (object) expected.Value, (object) precision, (object) num);
}
}
继 Michal 的优秀作品 post 之后,我开始使用以下内容,这显示出很好的前景:
[CustomAssertion]
public static void BeWithinPercentageOf(this NumericAssertions<double> value, double expected, double tolerance, string because = "", params object[] becauseArgs)
{
if (!Execute.Assertion.ForCondition(value.Subject != null)
.BecauseOf(because)
.FailWith($"Expected {{context:subject}} to be '{expected}' {{reason}} but found null"))
return;
var actual = (double)value.Subject;
var diff = Math.Abs(expected - actual);
if (diff > double.Epsilon)
{
var percent = Math.Round(100 / (expected / diff), 2);
Execute.Assertion.ForCondition(percent <= tolerance)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:value} to be {1} (±{2}%){reason}, but {0} differed by {3}%.", actual, expected, tolerance, percent);
}
}
有没有办法通过 FluentAssertions 来控制如何比较字典的值是否相等?
我有一本 class,其中一本 属性 是字典 (string/double)。我想比较 class 的两个实例(预期的和实际的),并为字典成员指定如何确定 "equality"。
假设我有一个 class,如图所示:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var t1 = new Thing();
t1.Name = "Bob";
t1.Values.Add("Hello", 100.111);
t1.Values.Add("There", 100.112);
t1.Values.Add("World", 100.113);
var t2 = new Thing();
t2.Name = "Bob";
t2.Values.Add("Hello", 100.111);
t2.Values.Add("There", 100.112);
t2.Values.Add("World", 100.1133);
t1.Should().BeEquivalentTo(t2);
}
}
public class Thing
{
public string Name { get; set; }
public Dictionary<string, double> Values { get; set; } = new Dictionary<string, double>();
}
我希望能够指定如何比较字典中的 "World" 条目。实际上,这些值可能会非常大,或者在小数点后 10 位以上(但之后不会)相同,但我想我可能需要说“如果差异小于 1% 则相同”。
我喜欢 FluentAssertions 告诉我成员及其不同原因的方式,并尝试了自定义 IAssertionRule(使用 Options lambda),但这似乎只比较了 class 属性,而不是字典的成员。
我不拥有被比较的 classes,因此无法覆盖 "Equal" 方法,而且我找不到指定自定义比较器 (IEquatable) 的方法 - 但我怀疑我会失去关于为什么它们不一样的流畅细节。
如果可能,但任何方法也适用于作为 Class 属性的双精度数(与字典中的值相反),那没问题。
谢谢。
BeApproximately
可用于在可接受的精度范围内比较双打。将它与配置所有双打精度一起使用应该满足所需的行为。
t1.Should().BeEquivalentTo(t2, options => options
.Using<double>(ctx =>
ctx.Subject.Should().BeApproximately(ctx.Expectation, ctx.Expectation * 0.01D))
.WhenTypeIs<double>()
);
根据 Nkosi 的回答,这是我正在使用的 BeApproximately 的一个示例(允许将 BeApproximately
与 decimal?
一起使用):
[CustomAssertion]
public static void BeApproximately(this NullableNumericAssertions<decimal> value, decimal? expected, decimal precision, string because = "",
params object[] becauseArgs)
{
if (expected == null)
value.BeNull(because);
else
{
if (!Execute.Assertion.ForCondition(value.Subject != null).BecauseOf(because)
.FailWith($"Expected {{context:subject}} to be '{expected}' {{reason}} but found null"))
return;
Decimal num = Math.Abs(expected.Value - (Decimal) value.Subject);
Execute.Assertion.ForCondition(num <= precision).BecauseOf(because, becauseArgs).FailWith("Expected {context:value} to approximate {1} +/- {2}{reason}, but {0} differed by {3}.", (object) value.Subject, (object) expected.Value, (object) precision, (object) num);
}
}
继 Michal 的优秀作品 post 之后,我开始使用以下内容,这显示出很好的前景:
[CustomAssertion]
public static void BeWithinPercentageOf(this NumericAssertions<double> value, double expected, double tolerance, string because = "", params object[] becauseArgs)
{
if (!Execute.Assertion.ForCondition(value.Subject != null)
.BecauseOf(because)
.FailWith($"Expected {{context:subject}} to be '{expected}' {{reason}} but found null"))
return;
var actual = (double)value.Subject;
var diff = Math.Abs(expected - actual);
if (diff > double.Epsilon)
{
var percent = Math.Round(100 / (expected / diff), 2);
Execute.Assertion.ForCondition(percent <= tolerance)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:value} to be {1} (±{2}%){reason}, but {0} differed by {3}%.", actual, expected, tolerance, percent);
}
}