Как динамически пропустить тест с помощью Xunit 2.0?

Xunit 1.9.x предоставляет пользователю пример DynamicSkipExample.cs, чтобы помочь ему настроить динамическое пропуски a [Fact].

Это оказалось весьма полезным при выполнении кросс-платформенной разработки. Это позволяет временно игнорировать тест, если он не может быть правильно запущен из-за базового контекста (ОС, файловой системы,...).

Однако этот пример был удален в commit 2deeff5 на пути к версии 2.0.

Как можно повторно реализовать такую ​​функциональность через одну из точек расширяемости Xunit 2.0?

Примечание. Проблема в этом вопросе была поднята в трекере xUnit. См. xunit/xunit # 250.

Ответы

Ответ 1

В конечном итоге это было исправлено xunit/samples.xunit # 8.

Удивительный @BradWilson сделал это доступным как полный и подробный образец в samples.xunit репозиторий.

Ответ 2

[Обновление: XUnit v2.0 (окончательная первоначальная версия) теперь доступна, и пропущенные тесты поддерживаются им напрямую. Используйте [Fact (Skip = "specific reason")] ]

Обратите внимание, что XUnit v2.0 не отправлен. Этот пример совместим с Xunit 2.0 beta5, который вы можете найти на nuget.org. Могут быть другие способы сделать это (так как это только пример, к которому я пришел).

1) Определите атрибут, который будет украшать ваши тесты.

/// <inheritdoc/>
[AttributeUsage( AttributeTargets.Method, AllowMultiple = false )]
[XunitTestCaseDiscoverer( "SkippableTestCaseDiscoverer", "assemblynamehere" )]
public sealed class SkippableTestAttribute : FactAttribute
{
    public SkippableTestAttribute() { }
}    

2) Создайте своего первооткрывателя. (Мы выполнили пример кода в https://github.com/xunit/xunit/blob/2d9ce6fbd75e91a69a0cc83e1bc3d4eab18b2c6c/src/xunit.execution/Sdk/Frameworks/TheoryDiscoverer.cs)

/// <summary>
/// Implementation of <see cref="IXunitTestCaseDiscoverer"/> that supports finding test cases
/// on methods decorated with <see cref="SkippableTestAttribute"/>.
/// </summary>
public class SkippableTestCaseDiscoverer : IXunitTestCaseDiscoverer
{
    /// <inheritdoc/>
    [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Needs to return test case." )]
    public IEnumerable<IXunitTestCase> Discover( ITestMethod testMethod, IAttributeInfo factAttribute )
    {
        // some of this code is from https://github.com/xunit/xunit/blob/2d9ce6fbd75e91a69a0cc83e1bc3d4eab18b2c6c/src/xunit.execution/Sdk/Frameworks/TheoryDiscoverer.cs
        if ( factAttribute.GetNamedArgument<string>( "Skip" ) != null )
            return new[] { new XunitTestCase( testMethod ) };

        var dataAttributes = testMethod.Method.GetCustomAttributes( typeof( DataAttribute ) );

        try
        {
            var results = new List<XunitTestCase>();

            foreach ( var dataAttribute in dataAttributes )
            {
                var discovererAttribute = dataAttribute.GetCustomAttributes( typeof( DataDiscovererAttribute ) ).First();
                var discoverer = ExtensibilityPointFactory.GetDataDiscoverer( discovererAttribute );
                if ( !discoverer.SupportsDiscoveryEnumeration( dataAttribute, testMethod.Method ) )
                    return new XunitTestCase[] { new XunitTheoryTestCase( testMethod ) };

// These lines are our "custom dynamic logic" that determines if we should skip the test.
                IEnumerable<object[]> data = discoverer.GetData( dataAttribute, testMethod.Method );

                if ( data == null )
                {
                    var test = new SkippableTestCase( testMethod );
                    test.SkipTest( "Test not configured with any " );
                    return new[] { test };
                }
                foreach ( var dataRow in data )
                {
                    // Attempt to serialize the test case, since we need a way to uniquely identify a test
                    // and serialization is the best way to do that. If it not serializable, this will
                    // throw and we will fall back to a single theory test case that gets its data
                    // at runtime.
                    var testCase = new XunitTestCase( testMethod, dataRow );
                    SerializationHelper.Serialize( testCase );
                    results.Add( testCase );
                }
            }

            if ( results.Count == 0 )
                results.Add( new LambdaTestCase( testMethod,
                                               () => { throw new InvalidOperationException( String.Format( "No data found for {0}.{1}", testMethod.TestClass.Class.Name, testMethod.Method.Name ) ); } ) );

            return results;
        }
        catch
        {
            return new XunitTestCase[] { new XunitTheoryTestCase( testMethod ) };
        }
    }
}

3) Создайте класс, который реализует IXunitTestCase (поскольку базовый класс по умолчанию не позволяет изменять причину пропусков).

// Class is similar to XunitTestCase 
[Serializable]
public class SkippableTestCase : TestMethodTestCase, IXunitTestCase 
{
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Obsolete("Called by the de-serializer", error: true)]
    public SkippableTestCase() { }

    /// <summary>
    /// Initializes a new instance of the <see cref="SkippableTestCase"/> class.
    /// </summary>
    /// <param name="testMethod">The test method this test case belongs to.</param>
    /// <param name="testMethodArguments">The arguments for the test method.</param>
    public SkippableTestCase(ITestMethod testMethod, object[] testMethodArguments = null)
        : base(testMethod, testMethodArguments) { }

    /// <inheritdoc />
    protected SkippableTestCase(SerializationInfo info, StreamingContext context)
        : base(info, context) { }

    public void SkipTest( string reason )
    {
        base.SkipReason = reason;
    }

    /// <inheritdoc/>
    public virtual Task<RunSummary> RunAsync( IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource )
    {
        return new XunitTestCaseRunner( this, DisplayName, SkipReason, constructorArguments, TestMethodArguments, messageBus, aggregator, cancellationTokenSource ).RunAsync();
    }
}

У вас есть много вариантов того, как вы хотите установить base.SkipReason. В этом примере был создан публичный метод.

В этом примере будут пропущены те тесты, у которых есть MemberDataAttribute, который не возвращает строки данных. Вы можете изменить его, чтобы вернуть SkippableTestCase в соответствии с вашими критериями. Например, это открытие пропускает тесты в воскресенье.

/// <inheritdoc/>
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Needs to return test case." )]
public IEnumerable<IXunitTestCase> Discover( ITestMethod testMethod, IAttributeInfo factAttribute )
{
    if ( DateTime.Today.DayOfWeek == DayOfWeek.Sunday )
    {
        var test = new SkippableTestCase( testMethod );
        test.SkipTest( "Test not configured with any " );
        return new[] { test };
    }
    else
    {
        return new[] { new XunitTestCase( testMethod ) };
    }
}

Ответ 3

Вы также можете найти SkippableFact в Nugget Manager, затем вы можете использовать все функции с помощью [SkippableFact]