Testuj parametryzację w xUnit.net podobnie jak w NUnit

107

Czy są jakieś środki w ramach xUnit.net podobne do następujących funkcji 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
}

Spowoduje to wygenerowanie 8 oddzielnych testów w interfejsie GUI 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"; }

Spowoduje to wygenerowanie 5 oddzielnych testów i automatyczne porównanie wyników ( Assert.Equal()).

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

Spowoduje to wygenerowanie 6 testów kombinatorycznych. Bezcenny.

Kilka lat temu wypróbowałem xUnit i pokochałem go, ale brakowało mu tych funkcji. Nie mogę bez nich żyć. Czy coś się zmieniło?

UserControl
źródło
Kompletny przewodnik, który wysyła złożone obiekty jako parametr do metod testowania złożonych typów w teście jednostkowym
Iman Bahrampour

Odpowiedzi:

139

xUnit oferuje sposób przeprowadzania testów sparametryzowanych poprzez coś, co nazywa się teoriami danych . Koncepcja jest równoważna tej, którą można znaleźć w NUnit, ale funkcjonalność, którą można uzyskać po wyjęciu z pudełka, nie jest tak kompletna.

Oto przykład:

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

W tym przykładzie xUnit uruchomi Should_format_the_currency_value_correctlytest raz za każdym InlineDataAttributerazem, przekazując określoną wartość jako argument.

Teorie danych to punkt rozszerzalności , którego można użyć do tworzenia nowych sposobów uruchamiania testów sparametryzowanych. Sposób, w jaki to się robi, polega na tworzeniu nowych atrybutów, które sprawdzają i opcjonalnie działają na argumentach i zwracają wartość metod testowych.

Można znaleźć dobry praktyczny przykład, jak teorie danych xUnit może zostać przedłużony w AutoFixture „s Autodata i InlineAutoData teorii.

Enrico Campidoglio
źródło
3
Najwyraźniej nie wolno używać literałów dziesiętnych jako parametrów atrybutów.
Sergii Volchkov
1
@RubenBartelink Twój link nie został znaleziony. Zamiast tego przejdź tutaj: blog.benhall.me.uk/2008/01/introduction-to-xunit-net-extensions
Ronnie Overby
9
Będziesz potrzebować xUnit.net: Extensions (NuGet Package) lub w przeciwnym razie [Theory]atrybut nie jest dostępny.
Daniel AA Pelsmaeker
4
Byłoby wspaniale, gdyby najbardziej polecany framework do testów jednostkowych .NET miał jakąś dokumentację ..
Isaac Kleinman
6
Google twierdzi, że Twoje odpowiedzi SO SĄ dokumentacją xUnit.
nathanchere,
55

Rzucę tutaj jeszcze jedną próbkę, na wypadek, gdyby komuś zaoszczędziło to trochę czasu.

[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);
}
Siedem
źródło
Czy zapomniałeś zamykającego nawiasu w drugiej linii?
cs0815
Przydatne, dzięki :)
Zeek2
21

Na pierwsze żądanie możesz postępować zgodnie z przykładami znajdującymi się tutaj .

Możesz skonstruować klasę statyczną zawierającą dane niezbędne do zbioru testów

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

Następnie za pomocą atrybutu MemberData zdefiniuj test jako taki

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

lub jeśli używasz C # 6.0,

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

Pierwszy argument MemberDataAttribute umożliwia zdefiniowanie elementu członkowskiego, którego używasz jako źródła danych, dzięki czemu masz dość dużą elastyczność przy ponownym użyciu.

LewisM
źródło
13

Zgodnie z tym artykułem w xUnit masz trzy opcje "parametryzacji":

  1. InlineData
  2. ClassData
  3. MemberData

Przykład InlineData

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

Przykład 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)
}

Przykład 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 },
    };
itim
źródło
12

Znalazłem bibliotekę, która zapewnia równoważną funkcjonalność [Values]atrybutowi NUnit o nazwie Xunit .

Pozwala określić wartości na poziomie parametrów:

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

Lub możesz niejawnie obliczyć minimalną liczbę wywołań, aby objąć wszystkie możliwe kombinacje:

[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
źródło
6

Wziąłem na pokład wszystkie odpowiedzi tutaj i dodatkowo skorzystałem z TheoryData<,>typów ogólnych XUnit, aby dać mi proste, łatwe do odczytania i bezpieczne definicje danych dla atrybutu `` MemberData '' w moim teście, jak na tym przykładzie:

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

Trzy przebiegi testów zaobserwowane w eksploratorze testów dla „Mojego pierwszego testu”


NB Korzystanie z VS2017 (15.3.3), C # 7 i XUnit 2.2.0 dla .NET Core

Piotr
źródło
To jest urocze.
Brett Rowberry