Как написать похожие тестовые примеры, не нарушая принцип единой ответственности?

Я создал один модульный тест для разных входов, чтобы убедиться, что выходы правильные.

[TestMethod]
    public void CountInversionTest()
    {
        #region Arrange
        int[] sourceArray = {4, 3, 2, 1};
        int correctInversionCount = 6;
        int[] sourceArray2 = { 1, 3, 5, 2, 4, 6};
        int correctInversionCount2 = 3;
        int[] sourceArray3 = { 5, 6, 2, 3, 1, 4, 7 };
        int correctInversionCount3 = 10;
        #endregion

        #region Act
        Sorter sorter = new Sorter();
        int inversionCount = sorter.CountInversion(sourceArray);
        int inversionCount2 = sorter.CountInversion(sourceArray2);
        int inversionCount3 = sorter.CountInversion(sourceArray3);
        #endregion

        #region Assert
        Assert.AreEqual(correctInversionCount, inversionCount);
        Assert.AreEqual(correctInversionCount2, inversionCount2);
        Assert.AreEqual(correctInversionCount3, inversionCount3);
        #endregion
    }

Поскольку кейсы очень похожи, я поместил их в один метод тестирования. Это нормально или нарушает принцип единой ответственности? Если это нарушает SRP, что может быть лучше?


person Baso    schedule 10.08.2016    source источник
comment
Обычно вы хотите, чтобы они были в отдельных тестах, чтобы было легче узнать, какой тест не прошел.   -  person itsme86    schedule 10.08.2016
comment
@ itsme86 Следует ли мне создавать разные TestMethod для каждого случая? если да, то каким должно быть правильное соглашение об именах для этих TestMethods? И разве это не повторяется?   -  person Baso    schedule 10.08.2016
comment
Почему вы хотите протестировать 3 случая? Есть ли какой-нибудь особый крайний случай, проверенный одним из них? Если да, укажите цель в названии отдельного тестового набора.   -  person Henk Holterman    schedule 10.08.2016
comment
Если вы делаете одно и то же во всех трех случаях, среды тестирования обычно позволяют украсить метод атрибутами для передачи параметра. Например, метод тестирования может принимать массив, а атрибут будет определять массивы для передачи. Например, проверьте это: github.com/nunit/docs/wiki/TestCase-Attribute   -  person itsme86    schedule 10.08.2016


Ответы (3)


С надлежащей структурой модульного тестирования, такой как xUnit.net, вы могли бы написать Параметризованный тест вместо этого:

[Theory]
[InlineData(new[] { 4, 3, 2, 1 }, 6)]
[InlineData(new[] { 1, 3, 5, 2, 4, 6 }, 3)]
[InlineData(new[] { 5, 6, 2, 3, 1, 4, 7 }, 10)]
public void ParameterizedCountInversionTest(int[] input, int expected)
{
    Sorter sut = new Sorter();
    var actual = sut.CountInversion(input);
    Assert.Equal(expected, actual);
}

Это запустит три теста вместо одного, что даст вам лучшую видимость того, какой конкретный тестовый пример не прошел (если какой-либо из них не прошел).

Так же тест стал более читабельным.

NUnit также имеет эту функцию, но MSTest нет (в последний раз я смотрел).

person Mark Seemann    schedule 10.08.2016

Принцип единой ответственности говорит нам, что у этого метода должна быть только одна причина для изменения. Таким образом, модульный тест должен тестировать один метод и должен изменяться только при изменении тестируемого метода. Вот это метод CountInversion(). Может ли CountInversion() метод измениться таким образом, что один из sourceArray входов должен измениться, но не другие? В этом случае входные данные должны быть разделены на отдельные тесты для соответствия SRP.

Как правило, один модульный тест может вызывать тестируемый метод несколько раз с разными входными данными. Как прокомментировал @ itsme86, среды тестирования обычно способствуют такому поведению, передавая аргументы модульному тесту.

person jaco0646    schedule 10.08.2016

Я решил ответить на свой вопрос

Так как я не использую сторонние фреймворки или библиотеки для тестирования, вместо этого я использую MSTest по умолчанию. Я закончил этим

    [TestMethod]
    public void CountInversionTestCase1()
    {
        CountInversionTest(new int[] { 4, 3, 2, 1 }, 6);
    }
    [TestMethod]
    public void CountInversionTestCase2()
    {
        CountInversionTest(new int[] { 1, 3, 5, 2, 4, 6 }, 3);
    }
    [TestMethod]
    public void CountInversionTestCase3()
    {
        CountInversionTest(new int[] { 5, 6, 2, 3, 1, 4, 7 }, 10);
    }

    public void CountInversionTest(int[] sourceArray, int expectedInversionCount)
    {
        #region Act
        Sorter sorter = new Sorter();
        long actualInversionCount = sorter.CountInversion(sourceArray);
        #endregion

        #region Assert
        Assert.AreEqual(expectedInversionCount, actualInversionCount);
        #endregion
    }

Это не лучшее решение, но оно удовлетворяет требованиям и не использует стороннюю библиотеку. Я надеюсь, что это поможет кому-нибудь.

person Baso    schedule 11.08.2016
comment
Лучше дать методам тестирования осмысленные имена, а не Case1, Case2, Case3. Дайте им имена, которые указывают, почему необходимы три случая, т.е. какая часть метода CountInversion() покрывает каждый случай, не охваченный другими случаями. - person jaco0646; 12.08.2016