Testen Sie die Parametrierung in xUnit.net ähnlich wie in NUnit

106

Gibt es Mittel im xUnit.net-Framework, die den folgenden Funktionen von NUnit ähneln?

[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
}

Dadurch werden 8 separate Tests in der NUnit-GUI generiert

[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"; }

Dadurch werden 5 separate Tests generiert und die Ergebnisse automatisch verglichen ( Assert.Equal()).

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

Dies erzeugt 6 kombinatorische Tests. Unbezahlbar.

Vor ein paar Jahren habe ich xUnit ausprobiert und es geliebt, aber es fehlten diese Funktionen. Kann nicht ohne sie leben. Hat sich etwas geändert?

Nutzerkontrolle
quelle
Eine vollständige Anleitung, die komplexe Objekte als Parameter an Testmethoden für komplexe Typen in Unit Test
sendet

Antworten:

138

xUnit bietet einen Weg zu laufen parametrisiert Tests durch die so genannte Daten Theorien . Das Konzept entspricht dem in NUnit, aber die Funktionalität, die Sie sofort erhalten, ist nicht so vollständig.

Hier ist ein Beispiel:

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

In diesem Beispiel führt xUnit den Should_format_the_currency_value_correctlyTest InlineDataAttributejedes Mal einmal aus, wenn der angegebene Wert als Argument übergeben wird.

Datentheorien sind ein Erweiterungspunkt , mit dem Sie neue Methoden zum Ausführen Ihrer parametrisierten Tests erstellen können. Dazu werden neue Attribute erstellt , die die Argumente und den Rückgabewert der Testmethoden überprüfen und optional darauf reagieren.

Sie können finden können ein gutes praktisches Beispiel, wie xUnit Daten Theorien erweitert werden AutoFixture ‚s Autodata und InlineAutoData Theorien.

Enrico Campidoglio
quelle
3
Anscheinend ist es nicht erlaubt , Dezimalliterale als Attributparameter zu verwenden.
Sergii Volchkov
1
@RubenBartelink Ihr Link wurde nicht gefunden. Gehen Sie stattdessen hier: blog.benhall.me.uk/2008/01/introduction-to-xunit-net-extensions
Ronnie Overby
9
Sie benötigen das xUnit.net: Extensions (NuGet-Paket), andernfalls ist das [Theory]Attribut nicht verfügbar.
Daniel AA Pelsmaeker
4
Es wäre großartig, wenn das am meisten empfohlene .NET-Unit-Test-Framework eine Dokumentation hätte.
Isaac Kleinman
6
Google sagt, dass Ihre SO-Antworten die xUnit-Dokumentation sind.
Nathanchere
55

Lassen Sie mich hier noch eine Probe werfen, nur für den Fall, dass es jemandem Zeit spart.

[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);
}
Sevenate
quelle
Haben Sie eine schließende Klammer in der 2. Zeile vergessen?
CS0815
Nützlich, danke :)
Zeek2
21

Bei Ihrer ersten Anfrage können Sie den Beispielen hier folgen .

Sie können eine statische Klasse erstellen, die die Daten enthält, die für eine Sammlung von Tests erforderlich sind

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; }
        }
    }
}

Definieren Sie dann mithilfe des MemberData-Attributs den Test als solchen

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

oder wenn Sie C # 6.0 verwenden,

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

Mit dem ersten Argument von MemberDataAttribute können Sie das Mitglied definieren, das Sie als Datenquelle verwenden, sodass Sie bei der Wiederverwendung einiges an Flexibilität haben.

LewisM
quelle
13

Gemäß diesem Artikel in xUnit haben Sie drei "Parametrisierungs" -Optionen:

  1. InlineData
  2. ClassData
  3. MemberData

InlineData-Beispiel

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

ClassData-Beispiel

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)
}

MemberData-Beispiel

[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 },
    };
itim
quelle
12

Ich habe eine Bibliothek gefunden, die dem NUnit- [Values]Attribut Xunit.Combinatorial entspricht .

Hier können Sie Werte auf Parameterebene angeben:

[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
}

Oder Sie können implizit die minimale Anzahl von Aufrufen ermitteln lassen, um alle möglichen Kombinationen abzudecken:

[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
}
Adam
quelle
6

Ich habe alle Antworten hier TheoryData<,>berücksichtigt und zusätzlich die generischen Typen von XUnit verwendet, um mir einfache, leicht zu lesende und sichere Datendefinitionen für das Attribut 'MemberData' in meinem Test zu geben, wie in diesem Beispiel dargestellt:

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

Drei Testläufe wurden vom Test Explorer für "Mein erster Test" beobachtet.


NB Verwenden von VS2017 (15.3.3), C # 7 und XUnit 2.2.0 für .NET Core

Peter
quelle
Das ist schön.
Brett Rowberry