2015-01-03 11 views
16

Xunit 1.9.x fornisce all'utente l'esempio DynamicSkipExample.cs per aiutarlo a configurare il salto dinamico di un [Fact].Come saltare dinamicamente un test con Xunit 2.0?

Ciò si è rivelato molto utile durante l'esecuzione di uno sviluppo multipiattaforma. Ciò consente di ignorare temporaneamente un test quando non può essere eseguito correttamente a causa del contesto sottostante (sistema operativo, file system, ...).

Tuttavia, questo esempio è stato eliminato in commit 2deeff5 sulla strada verso la versione 2.0.

Come si può ri-implementare tale funzionalità attraverso uno dei punti di estensibilità di Xunit 2.0?

Nota: Un problema relativo a questo argomento è stato generato nel tracker xUnit. Vedi xunit/xunit#250.

risposta

25

[Aggiornamento: XUnit v2.0 (RTM) è ora disponibile e i test ignorabili sono supportati direttamente da esso. Usa [Fact (Skip = "specific reason")] ]

noti che xUnit v2.0 non ha spedito. Questo esempio è compatibile con Xunit 2.0 beta5 che puoi trovare su nuget.org. Potrebbero esserci altri modi per ottenere questo risultato (poiché questo è solo l'esempio a cui sono arrivato).

1) Definire un attributo che decorerà i test.

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

2) Crea il tuo scopritore. (Abbiamo seguito l'esempio di codice a 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) creare una classe che implementa IXunitTestCase (come classe base predefinita non consente di modificare la ragione salto).

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

Hai molte opzioni per come vuoi impostare base.SkipReason. In questo esempio, è stato creato un metodo pubblico.

Questo esempio salterà i test che hanno un MemberDataAttribute che non restituisce righe di dati. Puoi modificarlo per restituire lo SkippableTestCase in base ai tuoi criteri. Ad esempio, questa scoperta salta i test di domenica.

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

Grazie per questo.Una delle grandi cose dell'implementazione iniziale era che lasciava che il test "decidesse" di essere saltato lanciando una SkipException. Potresti aggiornare la tua risposta per dimostrare come fare una cosa del genere? In molti casi, infatti, lo scopritore potrebbe non avere tutti i dati per decidere se saltare o meno. – nulltoken

+0

Per consentire ai test di saltare saltando un SkipException è necessario eseguire il rolling di un corridore personalizzato. Vorresti che dimostri un simile codice? Non saresti quindi in grado di eseguire i test con nessuno dei runner predefiniti (Console, VS, ecc.). –

+0

Dando un'occhiata a TestRunner.RunAsync() sembra che tu sia effettivamente corretto e che non c'è modo con la versione beta5-2785 di "saltare" il test a metà volo. Come tale, accetterò la tua risposta. Grazie mille per tutte le tue spiegazioni! – nulltoken

1

È anche possibile cercare SkippableFact nel Nugget Manager, quindi è possibile utilizzare tutte le funzioni con [SkippableFact]

+0

Ho usato questo: https://www.nuget.org/packages/Xunit.SkippableFact e il suo GitHub a https://github.com/AArnott/Xunit.SkippableFact –

Problemi correlati