Я довольно новичок в мире модульного тестирования, и я просто решил добавить тестовое покрытие для моего существующего приложения на этой неделе.
Это огромная задача, в основном из-за количества тестируемых классов, а также потому, что написание тестов для меня является новым.
Я уже написал тесты для нескольких классов, но теперь мне интересно, правильно ли я это делаю.
Когда я пишу тесты для метода, у меня возникает чувство переписывания во второй раз того, что я уже написал в самом методе.
Мои тесты кажутся настолько тесно связанными с методом (тестируя весь путь кода, ожидая, что некоторые внутренние методы будут вызываться несколько раз, с определенными аргументами), что кажется, что если я когда-либо произвожу рефакторинг метода, тесты не пройдут, даже если Окончательное поведение метода не изменилось.
Это просто чувство, и, как уже говорилось ранее, у меня нет опыта тестирования. Если бы некоторые более опытные тестировщики могли дать мне советы о том, как написать отличные тесты для существующего приложения, это было бы очень признательно.
Редактировать: я хотел бы поблагодарить Stack Overflow, у меня были отличные материалы менее чем за 15 минут, которые ответили больше часов онлайн-чтения, которые я только что сделал.
источник
Ответы:
Я думаю, что вы делаете это неправильно.
Модульный тест должен:
Не следует заглядывать внутрь метода, чтобы увидеть, что он делает, поэтому изменение внутренних компонентов не должно приводить к сбою теста. Вы не должны напрямую проверять, что частные методы вызываются. Если вы хотите узнать, тестируется ли ваш частный код, воспользуйтесь инструментом покрытия кода. Но не зацикливайтесь на этом: 100% охват не является обязательным требованием.
Если ваш метод вызывает публичные методы в других классах, и эти вызовы гарантируются вашим интерфейсом, то вы можете проверить, что эти вызовы выполняются с помощью фальшивой среды.
Вы не должны использовать сам метод (или любой внутренний код, который он использует) для динамического генерирования ожидаемого результата. Ожидаемый результат должен быть жестко запрограммирован в вашем тестовом примере, чтобы он не менялся при изменении реализации. Вот упрощенный пример того, что должен делать модульный тест:
Обратите внимание, что способ вычисления результата не проверяется - только то, что результат правильный. Продолжайте добавлять все больше и больше простых тестовых примеров, подобных приведенным выше, пока вы не охватите как можно больше сценариев. Используйте инструмент покрытия кода, чтобы увидеть, пропустили ли вы какие-либо интересные пути.
источник
Для модульного тестирования я обнаружил, что и Test Driven (сначала тесты, затем код), и код сначала, а затем тестирование очень полезно.
Вместо написания кода, затем написания теста. Напишите код, а затем посмотрите, что, по вашему мнению, должен делать код. Подумайте обо всех предполагаемых применениях этого и затем напишите тест для каждого. Я считаю написание тестов более быстрым, но более сложным, чем само кодирование. Тесты должны проверить намерение. Также подумайте о намерениях, которые вы обнаружите в случае написания теста. И, конечно же, во время написания тестов вы можете обнаружить, что одно из немногих применений вызывает ошибку (что я часто нахожу, и я очень рад, что эта ошибка не повредила данные и не прошла проверку).
Тем не менее, тестирование почти похоже на кодирование дважды. На самом деле у меня были приложения, в которых тестового кода (количества) было больше, чем кода приложения. Одним из примеров был очень сложный конечный автомат. Я должен был убедиться, что после добавления дополнительной логики, все это всегда работает во всех предыдущих случаях использования. И так как эти случаи были довольно трудными для наблюдения за кодом, я получил такой хороший набор тестов для этой машины, что был уверен, что он не сломается даже после внесения изменений, и тесты несколько раз спасли мою задницу , И поскольку пользователи или тестировщики находили ошибки с пропущенными или неучтенными случаями, угадайте, что добавлено в тесты и больше никогда не повторялось. Это действительно придало пользователям уверенности в моей работе, а также сделало все это очень стабильным. И когда это должно было быть переписано по соображениям производительности, угадайте, что,
Все простые примеры, такие как «
function square(number)
отлично» и все такое, и, вероятно, являются плохими кандидатами, чтобы тратить много времени на тестирование Те, которые выполняют важную бизнес-логику, - вот где важно тестирование. Проверьте требования. Не просто проверить сантехнику. Если требования меняются, то угадайте, что, тесты тоже должны.Тестирование не должно быть буквальным тестированием того, что функция foo вызывала функциональную панель 3 раза. Это не правильно. Проверьте правильность результата и побочных эффектов, а не внутреннюю механику.
источник
Стоит отметить, что повторное встраивание модульных тестов в существующий код намного сложнее, чем в первую очередь стимулирование создания этого кода с помощью тестов. Это один из самых больших вопросов в работе с устаревшими приложениями ... как провести модульное тестирование? Об этом уже много раз спрашивали (так что вы можете быть закрыты из-за дурацкого вопроса), и люди обычно оказываются здесь:
Перемещение существующего кода в Test Driven Development
Я рекомендую книгу с принятым ответом, но помимо этого в ответах содержится больше информации.
источник
Не пишите тесты, чтобы получить полное покрытие вашего кода. Написать тесты, которые гарантируют ваши требования. Вы можете обнаружить кодовые пути, которые не нужны. И наоборот, если они необходимы, они там, чтобы выполнить какое-то требование; найди, что это такое, и проверь требование (не путь).
Держите свои тесты маленькими: один тест за требование.
Позже, когда вам нужно внести изменения (или написать новый код), попробуйте сначала написать один тест. Только один. Тогда вы сделаете первый шаг в разработке, управляемой тестами.
источник
Модульное тестирование - это вывод, который вы получаете из функции / метода / приложения. Неважно, как получается результат, просто важно, что он правильный. Поэтому ваш подход к подсчету обращений к внутренним методам и тому подобное неверен. Я склоняюсь к тому, чтобы сесть и написать, что метод должен возвращать при определенных входных значениях или определенной среде, а затем написать тест, который сравнивает фактическое возвращаемое значение с тем, что я придумал.
источник
Попробуйте написать модульный тест, прежде чем писать метод, который он собирается тестировать.
Это определенно заставит вас думать немного иначе о том, как все делается. Вы не будете иметь представления о том, как этот метод будет работать, только о том, что он должен делать.
Вы должны всегда проверять результаты метода, а не то, как метод получает эти результаты.
источник
тесты должны улучшить ремонтопригодность. Если вы измените метод и пройдете тест, это может быть хорошо. С другой стороны, если вы смотрите на свой метод как на черный ящик, то не должно иметь значения, что находится внутри метода. Дело в том, что для некоторых тестов нужно что-то насмехаться, и в этих случаях вы действительно не можете воспринимать метод как черный ящик. Единственное, что вы можете сделать, это написать интеграционный тест - вы загружаете полностью инстанцированный экземпляр тестируемой службы и выполняете свою работу так, как если бы она работала в вашем приложении. Тогда вы можете рассматривать это как черный ящик.
Это потому, что вы пишете свои тесты после того, как написали свой код. Если бы вы сделали это наоборот (сначала написали тесты), этого бы не случилось.
источник