Esses testes de unidade de estilo de teste de linha podem ser aprimorados para seguir boas práticas de projeto de TDD?

O seguinte pode ser melhorado, para seguir boas práticas de projeto de TDD (nomear, usar listtests, projetar as classes) em qualquer uma das estruturas .NET TDD/BDD?

Além disso, existe uma maneira melhor em qualquer um dos frameworks de ter rowtests onde eu possa ter uma expectativa individual para cada linha, assim como eu faço nesse exemplo (NUnit)?

O sistema em teste aqui é a classe Constraint que pode ter vários intervalos de inteiros válidos. O teste testa o método NarrowDown que pode tornar os intervalos válidos menores com base em outra restrição.

[TestFixture]
internal class ConstraintTests
{
    [Test]
    public void NarrowDown_Works()
    {
        RowTest_NarrowDown(
            new Range[] { new Range(0, 10), new Range(20, 30), new Range(40, 50) },
            new Range[] { new Range(1, 9), new Range(21, 29), new Range(41, 49) },
            new Range[] { new Range(1, 9), new Range(21, 29), new Range(41, 49) });

        RowTest_NarrowDown(
            new Range[] { new Range(0, 10), new Range(20, 30), new Range(40, 50), new Range(60, 70) },
            new Range[] { new Range(1, 9), new Range(21, 29), new Range(41, 49) },
            new Range[] { new Range(1, 9), new Range(21, 29), new Range(41, 49) });

        RowTest_NarrowDown(
            new Range[] { new Range(0, 10), new Range(20, 30), new Range(40, 50) },
            new Range[] { new Range(1, 9), new Range(21, 29), new Range(41, 49), new Range(60, 70) });
    }

    private static void RowTest_NarrowDown(IEnumerable sut, IEnumerable context)
    {
        Constraint constraint = new Constraint(sut);
        Constraint result = constraint.NarrowDown(new Constraint(context));
        Assert.That(result, Is.Null);
    }

    private static void RowTest_NarrowDown(IEnumerable sut, IEnumerable context, IEnumerable expected)
    {
        Constraint constraint = new Constraint(sut);
        Constraint result = constraint.NarrowDown(new Constraint(context));
        Assert.That(result, Is.Not.Null);
        Assert.That(result.Bounds, Is.EquivalentTo(expected));
    }
}
1
Cada conjunto de entradas deve ser um teste independente. Se a lógica de vários testes for idêntica à entrada/saída, use "Testes parametrizados". NUnit usa atributos ou um método que fornece os diferentes conjuntos de entrada para um teste parametrizado. Cada conjunto é executado/relatado como um caso de teste diferente pelo corredor de teste.
adicionado o autor Gishu, fonte

2 Respostas

Primeiro, você poderia melhorar o nome do seu teste de unidade NarrowDown_Works é extremamente vago, e eu não posso dizer o que a classe em teste deveria estar fazendo.

Você tem muitas afirmações acontecendo e muitos dados, eu não posso dizer o que é importante. Tente quebrar seu teste em testes menores e será mais fácil nomeá-los também. Se possível, use uma afirmação por teste .

Sua construção de dados de teste é bastante complexa, considere o uso de correspondentes como NHamcrest para reduzir a quantidade de dados de asserção que você precisa em vez de usar Is.EquivalentTo .

Você também pode usar um construtor ou construtores de fábrica para tornar a inicialização mais simples para a classe Restrição mais simples em vez de passar em uma matriz de Ranges .

2
adicionado
+1 para a dica para NHamcrest
adicionado o autor bitbonk, fonte

Você deve usar uma abordagem orientada a dados com data factories (em NUnit-speak, eles são chamados de fontes de casos de teste ). Isso torna seus testes muito mais fáceis de ler, entender, modificar e manter (ou, em geral, muito mais limpo):

[TestFixture]
internal class ConstraintTests
{
    static object[] TwoRanges = 
    {
        new object[]
            {
                new[] { new Range(0, 10), new Range(20, 30), new Range(40, 50) },
                new[] { new Range(1, 9), new Range(21, 29), new Range(41, 49), new Range(60, 70) }
            }
    };

    static object[] ThreeRanges = 
    {
        new object[]
            {
                new[] { new Range(0, 10), new Range(20, 30), new Range(40, 50) },
                new[] { new Range(1, 9), new Range(21, 29), new Range(41, 49) },
                new[] { new Range(1, 9), new Range(21, 29), new Range(41, 49) }
            },
        new object[]
            {
                new[] { new Range(0, 10), new Range(20, 30), new Range(40, 50), new Range(60, 70) },
                new[] { new Range(1, 9), new Range(21, 29), new Range(41, 49) },
                new[] { new Range(1, 9), new Range(21, 29), new Range(41, 49) }
            }
    };

    [Test, TestCaseSource("TwoRanges")]
    public void NarrowDown_WhenCalledWithTwoRanges_GivesTheExpectedResult(IEnumerable sut, IEnumerable context)
    {
        Constraint constraint = new Constraint(sut);
        Constraint result = constraint.NarrowDown(new Constraint(context));
        Assert.That(result, Is.Null);
    }

    [Test, TestCaseSource("ThreeRanges")]
    public void NarrowDown_WhenCalledWithThreeRanges_GivesTheExpectedResult(IEnumerable sut, IEnumerable context, IEnumerable expected)
    {
        Constraint constraint = new Constraint(sut);
        Constraint result = constraint.NarrowDown(new Constraint(context));
        Assert.That(result, Is.Not.Null);
        Assert.That(result.Bounds, Is.EquivalentTo(expected));
    }
}

Veja o quanto mais simples seus métodos de teste se tornaram agora? Além disso, isso fará com que cada conjunto de dados da origem do caso de teste de origem seja executado em um teste separado, para que a coisa toda não falhe somente porque um conjunto de dados causa uma falha. Lembre-se: um teste deve declarar apenas uma coisa uma .

HTH!

0
adicionado