Цикл внутри юнит-теста - PullRequest
16 голосов
/ 01 мая 2011

Можем ли мы иметь цикл внутри модульного теста?

Мой метод возвращает IEnumerable<IEnumerable>, я хотел бы провести модульное тестирование этой логики, когда создается IEnumerable<IEnumerable>. По сути, я хочу проверить, соответствует ли количество элементов в IEnumerable ожидаемому.

Я не могу найти альтернативный способ проверки внутреннего IEnumerable без оператора зацикливания. Пожалуйста, дайте мне знать, если это хорошая практика.

Ответы [ 3 ]

22 голосов
/ 01 мая 2011

Нет технической причины, по которой вы не можете это сделать. Вы можете иметь несколько операторов Assert в модульном тесте. Наличие оператора Assert в цикле - это просто сокращенный способ иметь несколько операторов Assert в тесте.

Однако некоторые считают, что в модульном тесте должен быть только один оператор 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, и ваш модульный тест будет «пройден», тогда как вы, вероятно, предполагали, что он потерпит неудачу.

Чтобы исправить эту ситуацию, у вас может быть отдельный тест, удостоверяющий, что в IEnumerable имеется более 0 элементов (т. Е. Что GetProducts действительно возвращает некоторые продукты):

Assert.IsNotEmpty(products);
8 голосов
/ 01 мая 2011

Одна из причин, по которой следует избегать написания цикла в тесте, состоит в том, чтобы сделать тест кратким и читаемым с первого взгляда.Поскольку вы пометили вопрос с помощью 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"));

терпит неудачу с сообщением:

Ожидается: для всех элементов свойство Length равно 3 и все элементы String, содержащие "a" But was: <"abc", "cat","bit", "hat">

, но проходит, если вы измените "bit" на "bat".

Книга Тестовые шаблоны xUnit: рефакторинг тестового кода Жерара Месароша

имеет много отличных ответов на такие вопросы, как ваш.

2 голосов
/ 22 января 2014

Да, вы можете иметь петли в модульном тесте, но с осторожностью. Как упомянул Алекс Йорк, циклы приемлемы, если вы тестируете one вещь; то есть одно ожидание.

Если вы используете циклы, я рекомендую вам сделать две вещи:

  1. Как упоминалось выше, тестируйте непустой набор итераций. Итерация по пустому набору - ложное срабатывание Ложноположительные результаты являются бичом всех автоматических тестирований, потому что никто не проверяет дважды зеленый результат.
  2. Включите описание теста, которое описывает текущую итерацию. Как минимум, включите индекс итерации.

Вот пример из моего тестирования свойства объекта «Больше, чем».

[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]

Описание теста на уровне итерации таково, что вы получаете Сбои, такие как:

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 вещей, а не одного. Счетчик больше, чем больше, чем больше, и что все методы, которые делают эту оценку, должны быть согласованными.

...