Kunnen deze roeitests van eenheidstesten worden verbeterd om goede TDD-ontwerppraktijken te volgen?

Kan de volgende unittest worden verbeterd, om goede TDD-ontwerppraktijken te volgen (naamgeving, gebruik van roeitests, ontwerpen van de klassen) in een van de .NET TDD/BDD-frameworks?

Ook is er een betere manier in een van de kaders om roeitests te hebben waarbij ik een individuele verwachting kan hebben voor elke rij, net zoals ik het doe in dit (NUnit) -voorbeeld?

Het systeem dat hier wordt getest, is de klasse Constraint die meerdere reeksen van geldige gehele getallen kan hebben. De test test de NarrowDown -methode die de geldige bereiken kleiner kan maken op basis van een andere beperking.

[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
Elke set inputs moet een onafhankelijke test zijn. Als de logica van meerdere tests identiek is zonder de input-output, gebruik dan "Parameterized Tests". NUnit maakt gebruik van kenmerken of een methode die de verschillende invoersets levert voor een geparametriseerde test. Elke set wordt door de test loper als een andere testcase uitgevoerd/gerapporteerd.
toegevoegd de auteur Gishu, de bron

2 antwoord

Ten eerste kun je de naam van je unit-test NarrowDown_Works erg vaag maken, en ik kan niet zeggen wat de geteste klasse zou moeten doen.

Je hebt veel beweringen en veel gegevens, ik kan niet zeggen wat belangrijk is. Probeer uw test te onderbreken in kleinere tests en het zal ook gemakkelijker zijn om ze een naam te geven. Gebruik zo mogelijk één bevestiging per test .

Uw constructie van testgegevens is vrij complex, overweeg het gebruik van matchers zoals NHamcrest om de benodigde hoeveelheid assertiegegevens te verminderen in plaats van Is.EquivalentTo te gebruiken.

U kunt ook een constructor of fabrieksconstructeur gebruiken om de initialisatie eenvoudiger te maken voor de Constraint -klas eenvoudiger dan om een ​​reeks bereiken door te geven.

2
toegevoegd
+1 voor de hint naar NHamcrest
toegevoegd de auteur bitbonk, de bron

Gebruik een datagedreven aanpak met datafabrieken (in NUnit-speak heten ze testcasebronnen ). Dit maakt uw tests een stuk gemakkelijker te lezen, te begrijpen, te wijzigen en te onderhouden (of, meer in het algemeen, een stuk schoner):

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

Zie je hoeveel eenvoudiger je testmethoden nu zijn geworden? Dit zorgt er ook voor dat elke set met gegevens van de oorspronkelijke testcase in een afzonderlijke test wordt uitgevoerd, dus het hele ding zal niet alleen falen omdat één set gegevens een fout veroorzaakt. Let op: een test zou slechts één ding moeten bevestigen.

HTH!

0
toegevoegd