Должен ли я проверить, что методы не генерируют исключения?
Я делаю свои первые детские шаги с модульным тестированием и написал (среди прочего) эти два метода:
[TestCase]
public void InsertionSortedSet_AddValues_NoException()
{
var test = new InsertionSortedSet<int>();
test.Add(5);
test.Add(2);
test.Add(7);
test.Add(4);
test.Add(9);
}
[TestCase]
public void InsertionSortedSet_AddValues_CorrectCount()
{
var test = new InsertionSortedSet<int>();
test.Add(5);
test.Add(2);
test.Add(7);
test.Add(4);
test.Add(9);
Assert.IsTrue(test.Count == 5);
}
Действительно ли нужен метод NoException
? Если будет выбрано исключение, оно будет также запущено в методе CorrectCount
.
Я склоняюсь к тому, чтобы сохранить его как 2 тестовых примера (возможно, реорганизовать повторяющийся код в качестве другого метода), потому что тест должен проверять только на одну вещь, но, возможно, моя интерпретация неверна.
Ответы
Ответ 1
Чтобы выразить это самыми простыми словами, IMO-тестирование того, какой метод не делает, может быть очень скользким, так как вы можете придумать все больше и больше сценариев, когда думаете об этом. Идя другим путем, утверждая, что ваш код делает, который вы намеревались сделать, это в значительной степени цель модульного тестирования.
Есть два простых вопроса, которые обычно помогают мне выявить подозрительный тест и выяснить, имеет ли смысл какой-либо смысл:
- Какая часть желаемой функциональности - это тестирование?
- какое простое изменение я могу сделать в тестируемом классе, чтобы разбить тест?
Обратите внимание, что это чрезвычайно просто для решения этих вопросов, имеющих второй тест (_CorrectCount
). Мы действительно не видели код метода Add
, но мы можем, скорее всего, получить достойное предположение, что можно было бы изменить, чтобы прервать этот тест. Проверенная функциональность еще более очевидна. Ответы интуитивно понятны и кажутся быстрыми (что хорошо!).
Теперь попробуйте ответить на эти вопросы для первого теста (_NoException
). Он сразу вызывает новые вопросы (Является ли рабочий код действительной функциональностью? Разве это не очевидно? Разве это не подразумевается? Разве это не то, к чему мы всегда стремимся? Почему в конце нет утверждения??). По второму вопросу это еще хуже - нарушение этого теста, вероятно, потребует явного исключения исключений... что мы все согласны не с тем, чтобы идти.
Заключение
Прост. Второй тест - прекрасный пример хорошо написанного unit test. Он короткий, он проверяет одну вещь, ее можно легко понять. Первый тест не. Даже в том, что он так же короток и (что кажется) прост, он вводит новые вопросы (в то время как он действительно должен отвечать уже заявленным - добавляет ли Add
?). - и в результате приносит ненужную сложность.
Ответ 2
Это имеет смысл сделать метод, который проверяет, что тестируемый метод выдает исключения, когда вы ожидаете этого. В первом тесте, которое вы не утверждаете, нет, что второй тест еще не распространяется.
Ответ 3
Я всегда тестирую как рабочие, так и неработающие. Таким образом, вы подтверждаете, что код работает, возвращает правильные результаты, а также обрабатывает ошибки ожидаемым образом.
Ответ 4
Imo, если вы уверены, что в рабочем списке InsertionSortedSet
работает (я не уверен, откуда он), я бы пропустил тестирование InsertionSortedSet_AddValues_NoException
, как вы намереваетесь сделать, если это так необходимо.
Конечно, лучше проверить как можно больше.
Ответ 5
Здесь нет 100% правильного ответа.
С одной стороны, вы правы, один тест должен проверить на одну вещь.
Это особенно верно в случаях, когда один из тестов может измениться в будущем, и тогда вы не сможете точно знать, проверяете ли вы другой тест.
С другой стороны, вы создаете избыточность, так как оба теста фактически проверяют одно и то же.
Эта избыточность плоха только в тех случаях, когда ваши тесты занимают слишком много времени для запуска, но поскольку (как кажется) у вас есть только несколько тестов, это не должно быть проблемой.
Ответ 6
В первом тесте нет утверждения и ожидаемого исключения, я думаю, он должен быть реорганизован.
IMO, один тест должен проверить неправильное поведение (ожидая, что ошибка будет выбрана), а другая для правильного поведения (исключение не возвращается, хорошее значение возвращается).