Как я могу иметь настраиваемые утверждения с помощью Shouldly и поддерживать сообщения утверждения c, указанные на сайте вызова? - PullRequest
3 голосов
/ 06 мая 2020

Я использую отличную Shouldly библиотеку в своих тестах xUnit, и я обнаружил, что использую заданную последовательность утверждений в разных тестах, поэтому я объединяю их в новые методы расширения утверждений - но когда я это делаю, я теряю сообщения контекстного утверждения Shouldly.

Вот мой старый код, который работает с Shouldly, чтобы включать информацию на уровне источника и контекст сайта вызова в Shouldly ошибка утверждения:

[Fact]
public void Dict_should_contain_valid_Foobar_Bar_entry()
{
    IDictionary<String,Bar> dict = ...
    dict.TryGetValue( "Foobar", out Bar bar ).ShouldBeTrue();
    bar.ShouldNotBeNull();
    bar.ChildList.Count.ShouldBe( expected: 3 );
    bar.Message.ShouldBeNull();
}

[Fact]
public void Dict_should_contain_valid_Barbaz_Bar_entry()
{
    IDictionary<String,Bar> dict = ...
    dict.TryGetValue( "Barbaz", out Bar bar ).ShouldBeTrue();
    bar.ShouldNotBeNull();
    bar.ChildList.Count.ShouldBe( expected: 3 );
    bar.Message.ShouldBeNull();
}

Я преобразовал его в этот новый метод расширения в том же проекте:

public static void ShouldBeValidBar( this IDictionary<String,Bar> dict, String barName )
{
    dict.ShouldNotBeNull();
    dict.TryGetValue( barName, out Bar bar ).ShouldBeTrue();
    bar.ShouldNotBeNull();
    bar.ChildList.Count.ShouldBe( expected: 3 );
    bar.Message.ShouldBeNull();
}

Итак, мои тесты изменены на это:

[Fact]
public void Dict_should_contain_valid_Foobar_Bar_entry()
{
    IDictionary<String,Bar> dict = ...
    dict.ShouldBeValidBar( "Foobar" );
}

[Fact]
public void Dict_should_contain_valid_Barbaz_Bar_entry()
{
    IDictionary<String,Bar> dict = ...
    dict.ShouldBeValidBar( "Barbaz" );
}

... но теперь мои сообщения Shouldly assert не содержат никакой контекстной информации из Dict_should_contain_valid_Foobar_Bar_entry, а вместо этого содержат только контекст из ShouldBeValidBar.

Как я могу проинструктировать Shouldly игнорировать контекст ShouldBeValidBar и использовать вместо него родительский call-site?

1 Ответ

3 голосов
/ 06 мая 2020

TL; DR:

Добавьте атрибут [ShouldlyMethods] в свой собственный метод расширения assert классы (не отдельные методы расширения):

[ShouldlyMethods] // <-- This, right here!
public static class MyShouldlyAssertions
{
    public static void ShouldBeValidBar( this IDictionary<String,Bar> dict, String barName )
    {
        [...]
    }
}

Длинная версия :

После некоторого поиска в Google, и чтения статей о том, как Shouldly работает - и после прочтения источника секретного соуса Shouldly: SourceCodeTextGetter, я вижу что он определяет, какие записи в трассировке стека могут быть пропущены по наличию атрибута [ShouldlyMethods] (Shouldly.ShouldlyMethodsAttribute) в типе, содержащем метод в каждом кадре трассировки стека:

void ParseStackTrace(StackTrace trace)
{
    [...]

    while (ShouldlyFrame == null || currentFrame.GetMethod().IsShouldlyMethod())
    {
        if (currentFrame.GetMethod().IsShouldlyMethod())
            ShouldlyFrame = currentFrame;

        [...]
    }

    [...]
}
internal static bool IsShouldlyMethod(this MethodBase method)
{
    if (method.DeclaringType == null)
        return false;

    return
        method
            .DeclaringType
            .GetCustomAttributes( typeof(ShouldlyMethodsAttribute), true )
            .Any()
        ||
        (
            method.DeclaringType.DeclaringType != null
            && 
            method
                .DeclaringType
                .DeclaringType
                .GetCustomAttributes( typeof(ShouldlyMethodsAttribute), true )
                .Any()
        );
}

Итак, это просто вопрос добавления атрибута [ShouldlyMethods] в классы контейнера моих методов расширения:

[ShouldlyMethods]
public static class ShouldlyAssertionExtensions
{    
    public static void ShouldBeValidBar( this IDictionary<String,Bar> dict, String barName )
    {
        dict.ShouldNotBeNull();
        dict.TryGetValue( barName, out Bar bar ).ShouldBeTrue();
        bar.ShouldNotBeNull();
        bar.ChildList.Count.ShouldBe( expected: 3 );
        bar.Message.ShouldBeNull();
    }
}

И теперь мои ошибки assert имеют контекст вызова - сайт ShouldBeValidBar. Ура!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...