Глобальные параметры C# Fluent Asserts для ShouldBeEquivalentTo

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

person Hawk    schedule 19.12.2013    source источник
comment
Вызывается ли сам TestInitialize?   -  person Dennis Doomen    schedule 12.02.2014
comment
Да, TestInitialize вызывается. Я даже добавил фрагмент кода непосредственно в тест, прежде чем выполнять проверку, и, похоже, он не срабатывает. Обновление вопроса с полным тестовым блоком.   -  person Hawk    schedule 13.02.2014


Ответы (3)


На самом деле, вы можете. Фабрика конфигурации по умолчанию представлена ​​свойством static EquivalencyAssertionOptions<Test>.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;
};

Если вы хотите, вы можете получить текущее значение по умолчанию и спрятать его в какой-либо переменной, которую вы используете из своего фабричного метода.

person Dennis Doomen    schedule 20.12.2013
comment
Возможно, я делаю это неправильно, но я попытался добавить следующее к инициализации моего теста, но, похоже, он не вызывается при создании экземпляра фабрики. EquivalencyAssertionOptions‹DateTime›.Default = () =› { var config = defaultAssertionOptions(); config.Using‹DateTime›(ctx =› ctx.Subject.Should().BeCloseTo(ctx.Expectation)).WhenTypeIs‹DateTime›(); вернуть конфиг; }; - person Hawk; 12.02.2014
comment
Кроме того, предлагаемый код не компилируется, поскольку конструктор для EquivalencyAssertionOptions является закрытым. - person Hawk; 13.02.2014
comment
На самом деле мой пример был неправильным. Поскольку вы выполняете утверждение для экземпляра класса Test, вы должны переопределить параметры по умолчанию для этого конкретного класса, а не DateTime, который используется внутри. Я только что проверил это на вашем примере и обновил свой первоначальный ответ. - person Dennis Doomen; 14.02.2014
comment
Превосходно! Спасибо. - person Hawk; 17.02.2014

Теперь это можно сделать с помощью статического класса 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>()
);
person tony722    schedule 19.03.2015
comment
На всякий случай, если кто-то такой же глупый, как я, имейте в виду, что, будучи статическими, эти параметры сохраняются на протяжении всего тестового прогона (что, конечно же, было запросом ОП). У меня был тест, который устанавливал параметры для игнорирования сравнения эквивалентности свойств с определенным именем, предполагая, что это переназначается в каждом тесте. Это не так, каждый раз добавлялось новое правило, поэтому каждый тест использовал «список игнорирования» предыдущего теста, а также свой собственный. Урок выучен.... - person Ryan; 10.05.2016

Боюсь, самое близкое, к чему вы можете прийти, — это предоставить новые методы.

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);
}
person Erti-Chris Eelmaa    schedule 20.12.2013