Петля внутри unit test
Можно ли создать цикл внутри unit test?
Мой метод возвращает IEnumerable<IEnumerable>
, я хотел бы unit test эту логику, где создается IEnumerable<IEnumerable>
. В принципе, я хочу проверить, соответствует ли количество элементов в IEnumerable
.
Я не могу найти альтернативный способ протестировать внутренний IEnumerable
без инструкции цикла. Пожалуйста, дайте мне знать, если это хорошая практика.
Ответы
Ответ 1
Нет технической причины, по которой вы не можете этого сделать. Вы можете иметь несколько утверждений Assert в unit test. Наличие оператора Assert в цикле - это просто сокращенный способ иметь несколько тестов Assert в тесте.
Однако некоторые люди думают, что в unit test должен быть только один оператор Assert.
Я лично не согласен - я думаю, что тест должен протестировать одну вещь - и для этого иногда вам может потребоваться более одного утверждения Assert.
Если ваш метод возвращает IEnumerable для Product, и каждый Product содержит IEnumerable of Color, тогда я думаю, что следующий тест в порядке:
[Test]
public void All_products_should_have_some_colors()
{
var products = GetProducts();
foreach (var product in products)
{
Assert.IsNotEmpty(product.Colors);
}
}
Однако вам нужно знать, что если IEnumerable содержит 0 элементов, цикл никогда не будет выполнять какие-либо утверждения Assert, и ваш unit test будет "проходить", тогда как вы, вероятно, намеревались его сбой.
Чтобы исправить эту ситуацию, вы можете провести отдельный тест, убедившись, что в IEnumerable содержится более 0 элементов (т.е. что GetProducts действительно возвращает некоторые продукты):
Assert.IsNotEmpty(products);
Ответ 2
Одна из причин избежать написания цикла в тесте заключалась в том, чтобы держать анализ кратким и читаемым с первого взгляда. Поскольку вы отметили вопрос с помощью NUnit, и вы говорите, что просто хотите проверить, что количество элементов соответствует ожиданиям, подумайте над тем, чтобы сделать свои утверждения с помощью NUnit Constraints.
Например,
IEnumerable<IEnumerable<char>> someStrings = new[] { "abc", "cat", "bit", "hat" };
Assert.That(someStrings, Has.All.With.Length.EqualTo(3).And.All.Contains("a"));
завершается сбой:
Ожидаемое: свойство всех элементов Длина равна 3 и всем элементам Строка, содержащая "a" Но было: "abc", "cat", "bit", "hat" >
но передается, если вы измените бит на "bat".
Книга xUnit Test Patterns: тестовый код рефакторинга
Джерард Мезарос
имеет много замечательных ответов на такие вопросы, как ваш.
Ответ 3
Да, вы можете иметь петли в unit test, но с осторожностью. Как уже упоминалось Alex York, петли приемлемы, если вы протестируете одну вещь; т.е. одно ожидание.
Если вы используете циклы, я рекомендую вам выполнить две вещи:
- Как уже упоминалось выше, проверьте непустой набор итераций. Итерация над пустым множеством является ложным положительным. Ложные положительные результаты - это ошибка всего автоматического тестирования, потому что никто не проверяет зеленый результат.
- Включить описание теста, описывающее текущую итерацию. Как минимум, включите индекс итерации.
Вот пример из моего тестирования свойства Greater Than объекта.
[Test]
public void TestCompare_XtoY_GreaterThan()
{
int numObjects = mOrderedList.Count;
for (int i = 1; i < numObjects; ++i)
{
for (int j = 0; j < i; ++j)
{
string testDescription = string.Format("{0} is greater than {1} which implies\n {2}\n is greater than\n {3}"
, i, j
, mOrderedList[i], mOrderedList[j]
);
Assert.IsTrue(0 < mOrderedList[i].CompareTo(mOrderedList[j]), testDescription);
Assert.IsTrue(0 < mOrderedList[i].Compare(mOrderedList[i], mOrderedList[j]), testDescription);
Assert.IsTrue(0 < mOrderedList[j].Compare(mOrderedList[i], mOrderedList[j]), testDescription);
Assert.Greater(mOrderedList[i], mOrderedList[j], testDescription);
}
}
}
Я тестирую, что мой упорядоченный список не пуст в тестовой установке, используя:
[SetUp]
public void GeneralTestSetup()
{
// verify the iterated sources are not empty
string testDescription = string.Format("The ordered list of objects must have at least 3 elements, but instead has only {0}.", mOrderedList.Count);
Assert.IsTrue(2 < mOrderedList.Count, testDescription);
}
У меня есть несколько утверждений даже в моем цикле, но все утверждения тестируют одно ожидание:
if i > j then mOrderedList[i] > mOrderedList[j]
Описание теста на уровне итерации - это так: Failures, например:
10 is greater than 9 which implies
TestActionMethodInfo: [Actions.File, Version=1.0.446.0, File, VerifyReadOnly]
is greater than
TestActionMethodInfo: [Actions.File, Version=1.0.446.0, File, Write]
Expected: True
But was: False
вместо просто:
Expected: True
But was: False
Вопрос/дебаты о моем коде выше:
Я проверяю одну вещь?
Я утверждаю 4 различных метода сравнения внутри объекта, которые можно было бы утверждать как тестирование 4 вещей, а не один. Счетчик больше, чем больше, чем и что все методы, которые делают эту оценку, должны быть согласованными.