如何使用FluentAssertions控制词典成员的“平等”

时间:2018-06-23 22:16:04

标签: c# fluent-assertions

是否可以通过FluentAssertions控制字典中的值是否相等?

我有一个类,其中一个属性是字典(字符串/双精度)。我想比较该类的两个实例(预期实例和实际实例),并为字典成员指定如何确定“平等”。

假设我有一个如图所示的课程:

[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>();
}

我希望能够指定如何比较字典中的“世界”条目。实际上,这些值可能非常大,或者在十进制小数点后都是相同的(但是此后不是),但我认为我可能需要说些类似的话,“如果相差小于1%,则相同。”

我喜欢FluentAssertions告诉我成员的方式以及为什么它们不相同,并且尝试了自定义IAssertionRule(使用Option lambda),但这似乎只是比较类属性,而不是字典成员。 / p>

我不拥有要比较的类,因此无法覆盖“等于”方法,也无法找到指定自定义比较器(IEquatable)的方法-但我怀疑我会丢失为什么它们如此流利的详细信息不一样。

如果可能的话,但是任何方法也适用于作为Class属性的double(而不是字典中的值),就可以了。

谢谢。

3 个答案:

答案 0 :(得分:3)

BeApproximately可用于比较可接受范围内的精度。将其与配置所有双精度的精度一起使用应满足期望的行为。

t1.Should().BeEquivalentTo(t2, options => options
    .Using<double>(ctx => 
        ctx.Subject.Should().BeApproximately(ctx.Expectation, ctx.Expectation * 0.01D))
    .WhenTypeIs<double>()
);

引用Object graph comparison: Equivalency Comparison Behavior

答案 1 :(得分:1)

下面是Nkosi的答案,这是我正在使用的Be大约的一个示例(允许将BeApproximatelydecimal?一起使用):

    [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);
        }
    }

答案 2 :(得分:0)

根据Michal的出色文章,我开始从事以下工作,这显示出良好的前景:

    [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);
        }
    }