Использование Assert.Inconclusive
Вопрос
Мне интересно, как кому-то следует использовать Assert.Inconclusive().
Я использую его, если мой модульный тест может провалиться по причине, отличной от цели теста.
Например, у меня есть метод класса, который вычисляет сумму массива целых чисел.В том же классе есть также метод вычисления среднего значения элемента.Это реализуется путем вызова суммы и деления ее на длину массива.
Написать модульный тест для Sum() очень просто.Однако, когда я пишу тест для Average() и Sum() терпит неудачу, то и Average(), скорее всего, также потерпит неудачу.
Причина неудачи Average не указана явно;он потерпел неудачу по причине, отличной от той, по которой он должен проверяться.Вот почему я бы проверил, возвращает ли Sum() правильный результат, иначе я Assert.Inconclusive().
Можно ли считать это хорошей практикой?Для чего предназначен Assert.Inconclusive?Или мне лучше решить предыдущий пример с помощью Isolation Framework?
Решение
Когда я использую VS для создания модульных тестов, я получил Assert.Inclusive для сгенерированных методов тестирования, и обычно я меняю утверждение на что-то еще, когда работаю с ними.Я использую вопросительные знаки Assert.Inconclusive в результатах теста в качестве маркеров, позволяющих быстро определить, какие тесты я еще не завершил.
Ну, это просто мой способ использования.Судя по названию «Неокончательный», я думаю, вы можете использовать его для обозначения своего недетерминированного состояния, если документируете, что оно означает.
Однако, судя по описанию вашего Average()
метод, я думаю, что, возможно, ваш модульный тест недостаточно атомарен, чтобы охватить только один «модуль», один конкретный сценарий.Иногда я пишу 2 или 3 метода модульного тестирования для одного метода.Или ты можешь сломать свой Average()
метод к более мелким методам, охватывающим отдельные обязанности.Таким образом, вы можете протестировать эти более мелкие методы перед модульным тестированием. Average()
один.
Йоханнес,
Вот как я бы реализовал 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.
Надеюсь, это поможет прояснить идею.
Другие советы
Неокончательный тест - это тест, для которого вы не можете определить результат. Например, что если у вас есть тест, который использует какой-то внешний ресурс (например, подключение к Интернету). Если соединение в данный момент недоступно, это не значит, что тест не пройден. С другой стороны, вы не должны просто пометить его как успешный, не выполнив его. Таким образом, вы помечаете его как неокончательный, и это можно увидеть в протоколе испытаний.
ПРИМЕЧАНИЕ. В целом, вы не должны использовать такие внешние ресурсы в своих тестах, так как это может сделать тесты хрупкими. Р>
Для тестов, которые еще не были завершены, я использую атрибут Explicit
MbUnit.
Я только Assert.Incclusive на модульных тестах я еще не написал. Иногда, когда я пишу что-то, я понимаю какой-то угловой случай, который я не хочу пропустить. Поэтому я быстро перепрыгиваю, пишу тестовый метод с описательным именем и добавляю одну строку Assert.Inconclusive. Р>
Причина для этого заключается в том, что он предоставляет документацию по вещам, которые мне нужно протестировать, не слишком прерывая рабочий процесс. Это также позволяет быстро отфильтровывать неудачи теста в списке результатов. Наличие неубедительного отказа означает, что я ничего не сломал, я просто написал больше тестов. Р>
Assert.Inconclusive указывает, что либо:
Я еще не написал тест; я только создал тестовый метод
-или-
У моего теста есть зависимость, и эта зависимость недоступна.Например
List<Customer> custs = o.GetAllCustomers();
if (custs.Count == 0)
{
Assert.Inconclusive("No customers to test");
return;
}