web-dev-qa-db-fra.com

Comment sauter dynamiquement un test avec Xunit 2.0?

Xunit 1.9.x fournit à l'utilisateur l'exemple DynamicSkipExample.cs pour l'aider à configurer le saut dynamique d'un [Fact].

Cela s'est avéré très utile lors de l'exécution d'un développement multiplateforme. Cela permet à un test d'être temporairement ignoré lorsqu'il ne peut pas être correctement exécuté en raison du contexte sous-jacent (OS, système de fichiers, ...).

Cependant, cet exemple a été abandonné dans commit 2deeff5 sur la route de la version 2.0.

Comment peut-on réimplémenter une telle fonctionnalité à travers l'un des points d'extensibilité de Xunit 2.0?

Remarque: Un problème à ce sujet a été soulevé dans le tracker xUnit. Voir xunit/xunit # 25.

32
nulltoken

Ce problème a finalement été résolu xunit/samples.xunit # 8.

L'incroyable @ BradWilson l'a rendu disponible en tant qu'échantillon complet et détaillé dans le - samples.xunit référentiel.

3
nulltoken

[Mise à jour: XUnit v2.0 (RTM) est maintenant disponible et les tests désactivables sont pris en charge directement par lui. Utilisez [Fact (Skip = "specific reason")]]

Notez que XUnit v2.0 n'a pas été livré. Cet exemple est compatible avec Xunit 2.0 beta5 que vous pouvez trouver sur nuget.org. Il peut y avoir d'autres façons d'accomplir cela (car ce n'est que l'exemple auquel je suis arrivé).

1) Définissez un attribut qui décorera vos tests.

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

2) Créez votre découvreur. (Nous avons suivi l'exemple de code sur 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's 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) Créez une classe qui implémente IXunitTestCase (car la classe de base par défaut ne permet pas de modifier la raison du saut).

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

Vous disposez de nombreuses options pour définir la base.SkipReason. Dans cet exemple, une méthode publique a été créée.

Cet exemple ignore les tests qui ont un MemberDataAttribute qui ne renvoie aucune ligne de données. Vous pouvez le modifier pour renvoyer le SkippableTestCase en fonction de vos critères. Par exemple, cette découverte saute les tests le dimanche.

/// <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 ) };
    }
}
45
Matthew K

Vous pouvez également rechercher SkippableFact dans le gestionnaire de pépites, puis vous pouvez utiliser toutes les fonctions avec [SkippableFact]

6