Могут ли быть усовершенствованы эти модульные тесты стиля серии, чтобы следовать хорошей практике проектирования TDD?

Можно ли улучшить следующий подход, чтобы следовать хорошим методам проектирования TDD (именованию, использованию тестов строк, разработке классов) в любой платформе .NET TDD/BDD?

Кроме того, есть ли лучший способ в любой из фреймворков иметь rowtests, где у меня может быть индивидуальное ожидание для каждой строки, как я это делаю в этом примере (NUnit)?

Здесь тестируется система Constraint , которая может иметь несколько диапазонов допустимых целых чисел. Тест тестирует метод NarrowDown , который может сделать допустимые диапазоны меньше на основе другого ограничения.

[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
Каждый набор входов должен быть независимым тестом. Если логика нескольких тестов одинакова без ввода-вывода, используйте «Параметрированные тесты». NUnit использует атрибуты или метод, который предоставляет различные наборы ввода для параметризованного теста. Каждый набор выполняется/сообщается в качестве другого тестового примера тестовым бегуном.
добавлено автор Gishu, источник

2 ответы

Во-первых, вы можете улучшить имя вашего модульного теста NarrowDown_Works , очень неопределенно, и я не могу сказать, что должен делать этот класс.

У вас много утверждений и множество данных, я не могу сказать, что важно. Попробуйте разорвать ваш тест на более мелкие тесты , и их будет проще назвать. Если возможно, используйте одно утверждение за тест .

Ваше построение тестовых данных довольно сложно, подумайте об использовании сокетов , например NHamcrest для уменьшения объема данных утверждения, которые вам нужны, вместо использования Is.EquivalentTo .

Вы могли бы также использовать конструктор-конструктор или фабричные конструкторы для упростить инициализацию для класса Constraint , а не передавать в массиве Ranges .

2
добавлено
+1 для подсказки к NHamcrest
добавлено автор bitbonk, источник

Вы должны использовать подход, основанный на данных, с фабриками данных (в NUnit-talk, они называются тестовыми источниками ). Это делает ваши тесты намного легче читать, понимать, модифицировать и поддерживать (или, в более общем плане, намного чище):

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

Посмотрите, насколько проще теперь ваши методы тестирования? Кроме того, это приведет к тому, что каждый набор данных из исходного источника тестового случая будет запущен в отдельном тесте, поэтому все это не будет терпеть неудачу только потому, что один набор данных вызывает сбой. Помните: тест должен содержать только один .

НТН!

0
добавлено