Как мне написать модульный тест, когда класс для тестирования сложен?
Я пытаюсь использовать TDD для написания игры в нарды на C++ с использованием VS 2010.
Я настроил CxxTest для написания тестовых случаев.
Первый класс для тестирования
class Position
{
public:
...
...
bool IsSingleMoveValid(.....)
...
...
}
Я хотел бы написать тест для функции IsSingleMoveValid (), и я думаю, что тест должен доказать, что функция работает правильно. К сожалению, есть очень много случаев для тестирования, и даже если я протестирую несколько случаев, некоторые могут сбежать.
Что ты предлагаешь? Как TDD справляется с этими проблемами?
5 ответов
Несколько рекомендаций:
- Тестируйте регулярные случаи. В вашей проблеме: проверяйте законные ходы, которые вы ЗНАЕТЕ, действительны. Вы можете либо пойти простым путем и иметь только несколько тестовых случаев, либо вы можете написать цикл, генерирующий все возможные законные шаги, которые могут возникнуть в вашем приложении, и протестировать их все.
- Тест граничных случаев. Это не совсем применимо к вашей задаче, но для тестирования простых числовых функций вида
f(x)
откуда ты это знаешьx
должен лежать в диапазоне[x_min, x_max)
, вы бы обычно также проверитьf(x_min-1), f(x_min), f(x_max-1), f(x_max)
, (Это может быть актуально для настольных игр, если у вас есть внутреннее представление доски с краем переполнения вокруг него) - Проверьте известные ошибки. Если вы когда-либо сталкивались с легальным ходом, который не признается вашим
IsSingleMoveValid()
Вы добавляете это в качестве тестового примера и затем исправляете свой код. Полезно сохранять такие тестовые случаи для защиты от будущих регрессий (некоторые будущие добавления / модификации кода могут повторно представить эту ошибку, и тест ее поймает).
Покрытие тестами (процент строк кода, покрытых тестами) - это цель, которую можно рассчитать с помощью таких инструментов, как gcov. Вы должны провести собственный анализ затрат и выгод, насколько тщательно вы хотите протестировать свой код. Но для чего-то столь важного, как легальное обнаружение ходов в игровой программе, я бы посоветовал вам быть здесь бдительным.
Другие уже прокомментировали прекращение испытаний в небольших подтестах. Номенклатура для этого заключается в том, что такие изолированные функции тестируются с помощью модульного тестирования, тогда как сопоставление таких функций в коде более высокого уровня тестируется с помощью интеграционного тестирования.
Если вы пишете тесты, то проще всего сломать IsSingleMoveValid
функционировать в меньшие функции и тестировать их индивидуально.
Как правило, разбивая сложные классы на несколько простых классов, каждый из которых выполняет четко определенную задачу, которую легко протестировать.
Как вы можете видеть в Википедии, TDD - Test Driven Development - это сначала написание теста.
В вашем случае это означало бы установить все допустимые ходы и написать для них тестовую функцию. Затем вы пишете код для каждого из этих тестов, пока все тесты не пройдут.
... К сожалению, есть очень много случаев для проверки, и даже если я проверю несколько случаев, некоторые могут сбежать.
Как уже говорилось, когда функция слишком сложна, наступает время рефакторинга!
Я настоятельно рекомендую вам книгу " Рефакторинг - улучшение дизайна существующего кода" Мартина Фаулера с участием Кента Бека и других. Это учебник и справочник, что делает его очень ценным, на мой взгляд.
Это, вероятно, лучшая книга по рефакторингу, и она научит вас, как разделить свою функцию, не нарушая ничего. Кроме того, рефакторинг является действительно важным активом для TDD.:)
Не существует такого понятия, как "слишком много дел для проверки". Если код для обработки множества случаев может быть написан, их необходимо продумать. Если они могут быть написаны и продуманы, они могут написать код, который их проверяет. В среднем, для каждых 10 строк (тестируемого) кода, который вы пишете, вы можете добавить постоянный коэффициент тестирования кода, связанного с ним.
Конечно, вся хитрость заключается в том, чтобы знать, как писать код, соответствующий описанию, которое можно протестировать.
Следовательно, вам нужно начать с написания теста для всех случаев.
если есть большое, скажем, ради обсуждения, что у вас есть счетный набор возможных вариантов для тестирования (т. е. добавьте (n,m) == n+m для всех n и m целых чисел), но ваш фактический код действительно прост; вернуть n+m. Это, конечно, тривиально, но не стоит упускать из виду: вам не нужно тестировать все возможные ходы на доске, TDD стремится, чтобы ваши тесты охватывали весь код (т. Е. Тесты выполняют все ветви if в ваш код), не обязательно все возможные значения или комбинации состояний (которые экспоненциально велики)
проект с 80-90% покрытия строк означает, что ваши тесты используют 9 строк из каждых 10 строк вашего кода. В общем случае, если в вашем коде есть ошибка, это в большинстве случаев будет подтверждено при прохождении определенного пути кода.