ShouldBeEquivalentTo 的 C# Fluent Assertions 全局选项

Posted

技术标签:

【中文标题】ShouldBeEquivalentTo 的 C# Fluent Assertions 全局选项【英文标题】:C# Fluent Assertions global options for ShouldBeEquivalentTo 【发布时间】:2014-01-08 17:45:55 【问题描述】:

在 Fluent Assertions 中,比较具有 DateTime 属性的对象时,有时会在毫秒内出现轻微的不匹配,并且比较失败。我们绕过它的方法是像这样设置比较选项:

actual.ShouldBeEquivalentTo(expected,
        options =>
            options.Using<DateTime>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation))
                .WhenTypeIs<DateTime>());

有没有办法设置一次并始终应用它,而不是每次调用 ShouldBeEquivalentTo 时都必须指定它?

更新1: 尝试了以下方法,但它似乎不起作用,测试在 1 毫秒的差异上失败。工厂似乎没有调用新的默认值。

using System;
using FluentAssertions;
using FluentAssertions.Equivalency;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTestProject1

    class Test
    
        public DateTime TestDateTime  get; set; 
    

    [TestClass]
    public class UnitTest1
    
        [TestMethod]
        public void SettingFluentAssertionDefault()
        
            // arrange
            var defaultAssertionOptions = EquivalencyAssertionOptions<DateTime>.Default;

            EquivalencyAssertionOptions<DateTime>.Default = () =>
            
                var config = defaultAssertionOptions();
                config.Using<DateTime>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation)).WhenTypeIs<DateTime>();
                return config;
            ;

            var testDateTime = DateTime.Now;
            var expected = new Test TestDateTime = testDateTime;

            // act
            var actual = new Test TestDateTime = testDateTime.AddMilliseconds(1);

            // assert
            actual.ShouldBeEquivalentTo(expected);
        
    

【问题讨论】:

TestInitialize 本身是否被调用? 是的,调用了 TestInitialize。我什至在执行检查之前将代码 sn-p 直接添加到测试中,它似乎没有被命中。用完整的测试单元更新问题。 【参考方案1】:

现在这可以通过 AssertionOptions 静态类来完成。举个简单的例子:

[TestInitialize]
public void TestInit() 
  AssertionOptions.AssertEquivalencyUsing(options => options.ExcludingMissingMembers());

或如上例:

AssertionOptions.AssertEquivalencyUsing(options =>
  options.Using<DateTime>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation)).WhenTypeIs<DateTime>()
);

【讨论】:

以防万一有人像我一样愚蠢 - 请记住,这些选项是静态的,这些选项会在整个测试运行期间持续存在(这当然是 O.P. 的要求)。我有一个测试,它设置选项以忽略具有特定名称的属性的等效比较,假设在每个测试中都重新分配了它。不是,它每次都在添加一个新规则,所以每个测试都使用前一个测试的“忽略列表”以及它自己的。经验教训......【参考方案2】:

其实可以的。默认配置工厂由static 属性EquivalencyAssertionOptions&lt;Test&gt;.Default 公开。您可以轻松地为特定数据类型分配替代配置,或使用其他行为扩展默认配置。比如:

var defaultAssertionOptions = EquivalencyAssertionOptions<Test>.Default;

EquivalencyAssertionOptions<Test>.Default = () =>

    var config = defaultAssertionOptions();
    config.Using<DateTime>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation)).WhenTypeIs<DateTime>();
    return config;
;

如果您愿意,您可以获取当前默认值并将其隐藏在您从工厂方法中使用的某个变量中。

【讨论】:

也许我做错了,但我尝试将以下内容添加到我的测试初始化​​中,但在工厂实例化时似乎没有被调用。 EquivalencyAssertionOptions.Default = () => var config = defaultAssertionOptions(); config.Using(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation)).WhenTypeIs();返回配置; ; 此外,由于 EquivalencyAssertionOptions 的构造函数是私有的,因此建议的代码无法编译。 其实我的例子是错误的。由于您正在对 Test 类的实例执行断言,因此您必须覆盖该特定类的默认选项,而不是内部使用的 DateTime。我刚刚使用您的示例对此进行了测试并更新了我的原始答案。 太棒了!谢谢。【参考方案3】:

恐怕你能想到的最接近的事情就是提供新方法

public static void ShouldBeEquivalentToDef<T>(this T subject, object expectation, string reason = "",
    params object[] reasonArgs)

    ShouldBeEquivalentToDef(subject, expectation, config => config, reason, reasonArgs);


public static void ShouldBeEquivalentToDef<T>(this T subject, object expectation,
    Func<EquivalencyAssertionOptions<T>, EquivalencyAssertionOptions<T>> config, string reason = "", params object[] reasonArgs)

    var context = new EquivalencyValidationContext
    
        Subject = subject,
        Expectation = expectation,
        CompileTimeType = typeof (T),
        Reason = reason,
        ReasonArgs = reasonArgs
    ;

    var defConstructedOptions = config(EquivalencyAssertionOptions<T>.Default());
    defConstructedOptions.Using<DateTime>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation))
            .WhenTypeIs<DateTime>()

    new EquivalencyValidator(defConstructedOptions).AssertEquality(context);

【讨论】:

以上是关于ShouldBeEquivalentTo 的 C# Fluent Assertions 全局选项的主要内容,如果未能解决你的问题,请参考以下文章

忽略 ShouldBeEquivalentTo 中的内部属性

Fluentassertions.ShouldBeEquivalentTo 中的 Ignoredatamember

当主题是 DateTime 时,ShouldBeEquivalentTo 对等效对象失败

使用 ShouldBeEquivalentTo 时如何排除 IEnumerable 中所有项目的属性?

为啥 FluentAssertions ShouldBeEquivalentTo 验证内部?

Fluent Assertions ShouldBeEquivalentTo 总是以不同的属性传递