Prueba de parametrización en xUnit.net similar a NUnit

106

¿Existe algún medio en el marco xUnit.net similar a las siguientes características de 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
}

Esto generará 8 pruebas separadas en NUnit GUI

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

Esto generará 5 pruebas separadas y comparará automáticamente los resultados ( Assert.Equal()).

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

Esto generará 6 pruebas combinatorias. No tiene precio.

Hace unos años probé xUnit y me encantó, pero carecía de estas características. No puedo vivir sin ellos. ¿Ha cambiado algo?

Control de usuario
fuente
Una guía completa que envía objetos complejos como parámetro a los métodos de prueba de tipos complejos en la prueba unitaria
Iman Bahrampour

Respuestas:

138

xUnit ofrece una forma de ejecutar pruebas parametrizadas a través de algo llamado teorías de datos . El concepto es equivalente al que se encuentra en NUnit pero la funcionalidad que obtienes de la caja no es tan completa.

He aquí un ejemplo:

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

En este ejemplo, xUnit ejecutará la Should_format_the_currency_value_correctlyprueba una vez por InlineDataAttributecada vez pasando el valor especificado como argumento.

Las teorías de datos son un punto de extensibilidad que puede utilizar para crear nuevas formas de ejecutar sus pruebas parametrizadas. La forma en que se hace esto es creando nuevos atributos que inspeccionan y, opcionalmente, actúan sobre los argumentos y el valor de retorno de los métodos de prueba.

Se puede encontrar un buen ejemplo práctico de cómo las teorías de datos de xUnit pueden extenderse en AutoFixture 's AutoData y InlineAutoData teorías.

Enrico Campidoglio
fuente
3
Aparentemente, no está permitido usar literales decimales como parámetros de atributo.
Sergii Volchkov
1
@RubenBartelink su enlace no se encuentra. Vaya aquí en su lugar: blog.benhall.me.uk/2008/01/introduction-to-xunit-net-extensions
Ronnie Overby
9
Necesitará xUnit.net: Extensions (Paquete NuGet) o, de lo contrario, el [Theory]atributo no está disponible.
Daniel AA Pelsmaeker
4
Sería genial si el marco de prueba unitario .NET más recomendado tuviera alguna documentación.
Isaac Kleinman
6
Google dice que sus respuestas SO SON la documentación de xUnit.
nathanchere
55

Permítanme lanzar una muestra más aquí, por si acaso le ahorra algo de tiempo a alguien.

[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
fuente
¿Olvidó un paréntesis de cierre en la segunda línea?
cs0815
Útil, gracias :)
Zeek2
21

En su primera solicitud, puede seguir los ejemplos que se encuentran aquí .

Puede construir una clase estática que contenga los datos necesarios para una colección de pruebas.

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

Luego, usando el atributo MemberData, defina la prueba como tal

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 si está usando C # 6.0,

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

El primer argumento de MemberDataAttribute le permite definir el miembro que usa como fuente de datos, por lo que tiene una buena cantidad de flexibilidad en la reutilización.

LewisM
fuente
13

Según este artículo de xUnit, tiene tres opciones de "parametrización":

  1. InlineData
  2. ClassData
  3. MemberData

Ejemplo de InlineData

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

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

Ejemplo de 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
fuente
12

Encontré una biblioteca que produce una funcionalidad equivalente al [Values]atributo de NUnit llamado Xunit.Combinatorial :

Le permite especificar valores a nivel de parámetro:

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

O puede hacer que implícitamente calcule el número mínimo de invocaciones para cubrir todas las combinaciones posibles:

[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
}
Adán
fuente
6

Acepté todas las respuestas aquí y, además, utilicé los TheoryData<,>tipos genéricos de XUnit para brindarme definiciones de datos seguras, fáciles de leer y escribir para el atributo 'MemberData' en mi prueba, según este ejemplo:

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

Tres ejecuciones de pruebas observadas desde el explorador de pruebas para 'Mi primera prueba'


NB con VS2017 (15.3.3), C # 7 y XUnit 2.2.0 para .NET Core

Pedro
fuente
Esto es adorable.
Brett Rowberry