Testare la parametrizzazione in xUnit.net simile a NUnit


106

Esistono mezzi nel framework xUnit.net simili alle seguenti funzionalità di NUnit?

[Test, TestCaseSource("CurrencySamples")]
public void Format_Currency(decimal value, string expected){}

static object[][] CurrencySamples = new object[][]
{
    new object[]{ 0m, "0,00"},
    new object[]{ 0.0004m, "0,00"},
    new object[]{ 5m, "5,00"},
    new object[]{ 5.1m, "5,10"},
    new object[]{ 5.12m, "5,12"},
    new object[]{ 5.1234m, "5,12"},
    new object[]{ 5.1250m, "5,13"}, // round
    new object[]{ 5.1299m, "5,13"}, // round
}

Questo genererà 8 test separati nella GUI di NUnit

[TestCase((string)null, Result = "1")]
[TestCase("", Result = "1")]
[TestCase(" ", Result = "1")]
[TestCase("1", Result = "2")]
[TestCase(" 1 ", Result = "2")]
public string IncrementDocNumber(string lastNum) { return "some"; }

Questo genererà 5 test separati e confronterà automaticamente i risultati ( Assert.Equal()).

[Test]
public void StateTest(
    [Values(1, 10)]
    int input,
    [Values(State.Initial, State.Rejected, State.Stopped)]
    DocumentType docType
){}

Questo genererà 6 test combinatori. Inestimabile.

Qualche anno fa ho provato xUnit e l'ho adorato, ma mancava di queste caratteristiche. Non posso vivere senza di loro. Qualcosa è cambiato?


Una guida completa che invia oggetti complessi come parametro ai metodi di test tipi complessi in Unit test
Iman Bahrampour

Risposte:


138

xUnit offre un modo per eseguire test parametrizzati attraverso qualcosa chiamato teorie dei dati . Il concetto è equivalente a quello trovato in NUnit ma la funzionalità che ottieni fuori dalla scatola non è così completa.

Ecco un esempio:

[Theory]
[InlineData("Foo")]
[InlineData(9)]
[InlineData(true)]
public void Should_be_assigned_different_values(object value)
{
    Assert.NotNull(value);
}

In questo esempio xUnit eseguirà il Should_format_the_currency_value_correctlytest una volta ogni InlineDataAttributevolta che passa il valore specificato come argomento.

Le teorie dei dati sono un punto di estensibilità che puoi utilizzare per creare nuovi modi per eseguire i tuoi test parametrizzati. Il modo in cui questo viene fatto è creare nuovi attributi che ispezionano e opzionalmente agiscono sugli argomenti e restituiscono il valore dei metodi di test.

È possibile trovare un buon esempio pratico di come le teorie di dati di xUnit possono essere estesi in AutoFixture s' Autodata e InlineAutoData teorie.


3
Apparentemente, non è consentito utilizzare valori letterali decimali come parametri di attributo.
Sergii Volchkov

1
@RubenBartelink il tuo link non è stato trovato. Vai qui invece: blog.benhall.me.uk/2008/01/introduction-to-xunit-net-extensions
Ronnie Overby

9
Avrai bisogno di xUnit.net: Extensions (NuGet Package) o altrimenti l' [Theory]attributo non è disponibile.
Daniel AA Pelsmaeker

4
Sarebbe fantastico se il framework di unit test .NET più consigliato avesse un po 'di documentazione ..
Isaac Kleinman

6
Google dice che le tue risposte SO SONO la documentazione di xUnit.
nathanchere

55

Consentitemi di lanciare un altro campione qui, nel caso facesse risparmiare tempo a qualcuno.

[Theory]
[InlineData("goodnight moon", "moon", true)]
[InlineData("hello world", "hi", false)]
public void Contains(string input, string sub, bool expected)
{
    var actual = input.Contains(sub);
    Assert.Equal(expected, actual);
}

Hai dimenticato una parentesi di chiusura nella 2a riga?
cs0815

Utile, grazie :)
Zeek2

21

Alla tua prima richiesta, puoi seguire gli esempi che trovi qui .

È possibile costruire una classe statica contenente i dati necessari per una raccolta di test

using System.Collections.Generic;

namespace PropertyDataDrivenTests
{
    public static class DemoPropertyDataSource
    {
        private static readonly List<object[]> _data = new List<object[]>
            {
                new object[] {1, true},
                new object[] {2, false},
                new object[] {-1, false},
                new object[] {0, false}
            };

        public static IEnumerable<object[]> TestData
        {
            get { return _data; }
        }
    }
}

Quindi, utilizzando l'attributo MemberData, definire il test come tale

public class TestFile1
{
    [Theory]
    [MemberData("TestData", MemberType = typeof(DemoPropertyDataSource))]
    public void SampleTest1(int number, bool expectedResult)
    {
        var sut = new CheckThisNumber(1);
        var result = sut.CheckIfEqual(number);
        Assert.Equal(result, expectedResult);
    }
}

o se stai usando C # 6.0,

[Theory]
[MemberData(nameof(PropertyDataDrivenTests.TestData), MemberType = typeof(DemoPropertyDataSource))]

Il primo argomento di MemberDataAttribute ti consente di definire il membro che usi come origine dati, in modo da avere una discreta flessibilità nel riutilizzo.


13

Secondo questo articolo in xUnit hai tre opzioni di "parametrizzazione":

  1. InlineData
  2. ClassData
  3. MemberData

Esempio di InlineData

[Theory]
[InlineData(1, 2)]
[InlineData(-4, -6)]
[InlineData(2, 4)]
public void FooTest(int value1, int value2)
{
    Assert.True(value1 + value2 < 7)
}

Esempio di ClassData

public class BarTestData : IEnumerable<object[]>
{
    public IEnumerator<object[]> GetEnumerator()
    {
        yield return new object[] { 1, 2 };
        yield return new object[] { -4, -6 };
        yield return new object[] { 2, 4 };
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}


[Theory]
[ClassData(typeof(BarTestData))]
public void BarTest(int value1, int value2)
{
    Assert.True(value1 + value2 < 7)
}

Esempio MemberData

[Theory]
[MemberData(nameof(BazTestData))]
public void BazTest(int value1, int value2)
{
    Assert.True(value1 + value2 < 7)
}

public static IEnumerable<object[]> BazTestData => new List<object[]>
    {
        new object[] { 1, 2 },
        new object[] { -4, -6 },
        new object[] { 2, 4 },
    };

12

Ho trovato una libreria che produce funzionalità equivalenti [Values]all'attributo di NUnit chiamato Xunit.Combinatorial :

Ti consente di specificare valori a livello di parametro:

[Theory, CombinatorialData]
public void CheckValidAge([CombinatorialValues(5, 18, 21, 25)] int age, 
    bool friendlyOfficer)
{
    // This will run with all combinations:
    // 5  true
    // 18 true
    // 21 true
    // 25 true
    // 5  false
    // 18 false
    // 21 false
    // 25 false
}

Oppure puoi implicitamente far capire il numero minimo di invocazioni per coprire tutte le possibili combinazioni:

[Theory, PairwiseData]
public void CheckValidAge(bool p1, bool p2, bool p3)
{
    // Pairwise generates these 4 test cases:
    // false false false
    // false true  true
    // true  false true
    // true  true  false
}

6

Ho preso in considerazione tutte le risposte qui e ho inoltre utilizzato TheoryData<,>i tipi generici di XUnit per fornirmi definizioni di dati semplici, facili da leggere e digitate sicure per l'attributo 'MemberData' nel mio test, come in questo esempio:

/// must be public & static for MemberDataAttr to use
public static TheoryData<int, bool, string> DataForTest1 = new TheoryData<int, bool, string> {
    { 1, true, "First" },
    { 2, false, "Second" },
    { 3, true, "Third" }
};

[Theory(DisplayName = "My First Test"), MemberData(nameof(DataForTest1))]
public void Test1(int valA, bool valB, string valC)
{
    Debug.WriteLine($"Running {nameof(Test1)} with values: {valA}, {valB} & {valC} ");
}

Tre test eseguiti da Esplora test per "Il mio primo test"


NB Utilizzando VS2017 (15.3.3), C # 7 e XUnit 2.2.0 per .NET Core


È adorabile.
Brett Rowberry
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.