Использование Assert.Inconclusive - PullRequest
24 голосов
/ 02 августа 2009

Мне интересно, как кто-то должен использовать Assert.Inconclusive ().

Я использую его, если мой модульный тест потерпит неудачу не по той причине, для которой предназначен тест.

Например, у меня есть метод для класса, который вычисляет сумму массива целых чисел. В том же классе есть также метод для вычисления среднего значения элемента. Это реализуется путем вызова суммы и деления ее на длину массива.

Написание модульного теста для Sum () просто. Однако, когда я пишу тест для Average () и Sum () завершается неудачей, то Average () также может завершиться ошибкой.

Ошибка среднего не является явной причиной ее отказа; он потерпел неудачу по причине, отличной от того, что он должен проверить. Вот почему я бы проверил, если Sum () возвращает правильный результат, в противном случае я Assert.Inconclusive ().

Это считается хорошей практикой? Для чего предназначен Assert.Inconclusive? Или мне лучше решить предыдущий пример с помощью Isolation Framework?

Ответы [ 4 ]

25 голосов
/ 02 августа 2009

Неокончательный тест - это тест, по которому вы не можете определить результат. Например, что если у вас есть тест, который использует какой-то внешний ресурс (например, подключение к Интернету). Если соединение в данный момент недоступно, это не значит, что тест не пройден. С другой стороны, вы не должны просто пометить его как успешный, не выполнив его. Таким образом, вы помечаете его как неокончательный, и это можно увидеть в протоколе испытаний.

ПРИМЕЧАНИЕ. В целом, вы не должны использовать такие внешние ресурсы в своих тестах, так как это может сделать тесты хрупкими.

Для тестов, которые еще не были завершены, я использую атрибут MbUnit Explicit.

15 голосов
/ 02 августа 2009

Когда я использую VS для генерации модульных тестов, я получаю Assert.Inclusive для сгенерированных тестовых методов и обычно я изменяю утверждение, когда я работаю с ними, что-то другое. Я использую вопросительные знаки Assert.Inconclusive в результатах теста в качестве маркеров, чтобы быстро сказать мне, какие тесты я еще не закончил.

Ну, я просто использую это. Из его названия «Неокончательный», я думаю, вы можете использовать, чтобы указать свое недетерминированное состояние, пока вы документируете его значение.

Однако, из описания вашего Average() метода, я думаю, что, возможно, ваш модульный тест недостаточно атомарен, чтобы охватить только одну «единицу», один конкретный сценарий. Иногда я пишу 2 или 3 метода модульного тестирования для одного метода. Или вы можете разбить свой метод Average() на более мелкие методы, охватывающие отдельные обязанности. Таким образом, вы можете тестировать эти меньшие методы перед модульным тестированием Average() one.


Johannes

Так я бы реализовал методы Sum() и Average().

public static class MyMath
{
    private static void ValidateInput(ICollection<int> numbers)
    {
        if (numbers == null)
            throw new ArgumentNullException("numbers", "Null input.  Nothing to compute!");
        if (numbers.Count == 0)
            throw new ArgumentException("Input is empty.  Nothing to compute!");
    }

    public static int Sum(int[] numbers)
    {
        ValidateInput(numbers);

        var total = 0;
        foreach (var number in numbers)
            total += number;

        return total;
    }

    public static double Average(int[] numbers)
    {
        ValidateInput(numbers);
        return Sum(numbers) / numbers.Length;
    }
}

Для простоты я просто выкидываю ArgumentException исключения из метода ValidateInput(ICollection<int>). Вы также можете проверить возможность переполнения и выбросить OverflowException в методе ValidateInput(ICollection<int>).

С учетом вышесказанного, вот как я бы протестировал функцию Average(int[]).

[TestMethod]
public void AverageTest_GoodInput()
{
    int[] numbers = {1, 2, 3};
    const double expected = 2.0;
    var actual = MyMath.Average(numbers);
    Assert.AreEqual(expected, actual);
}

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void AverageTest_NullInput()
{
    int[] numbers = null;
    MyMath.Average(numbers);
}

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void AverageTest_EmptyInput()
{
    var numbers = new int[0];
    MyMath.Average(numbers);
}

С этими настройками тестов я могу быть уверен, что когда все тесты пройдут, моя функция верна. Ну, кроме случая переполнения. Теперь я могу вернуться к методу ValidateInput(ICollection<int>), чтобы добавить логику для проверки переполнения, а затем добавить еще один тест, чтобы ожидать, что OverflowException будет выброшено для вида входных данных, которые вызывают переполнение. Или делайте это в обратном порядке, если вам нравится подход TDD.

Надеюсь, это поможет прояснить идею.

9 голосов
/ 02 августа 2009

Я только Assert.Incclusive на модульных тестах я еще не написал. Иногда, когда я пишу что-то, я понимаю какой-то угловой случай, который я не хочу пропустить. Поэтому я быстро перепрыгиваю, пишу тестовый метод с описательным именем и добавляю одну строку Assert.Inconclusive.

Причина для этого заключается в том, что она предоставляет документацию по вещам, которые мне нужно протестировать, не слишком прерывая рабочий процесс. Это также позволяет быстро отфильтровывать неудачи теста в списке результатов. Наличие неубедительного отказа означает, что я ничего не сломал, я просто написал больше тестов.

7 голосов
/ 04 мая 2010

Assert.Inconclusive указывает, что либо:

Я еще не написал тест, я только создал метод теста

-или-

Мой тест имеет зависимость, и эта зависимость недоступна. Например

List<Customer> custs = o.GetAllCustomers();
if (custs.Count == 0)
{
  Assert.Inconclusive("No customers to test");
  return;
}
...