Я надеюсь, что как грамотный разрабочик, вы имеете представление о unit-тестировании и сделаете себе в голове пару мысленных отметок о том, чего надо избегать при написании тестов. Знакомьтесь:
Unit-тест, который успешно выполняет все кейсы и выглядит работающим правильно, однако при более детальном рассмотрении обнаруживается, что он на самом деле не тестирует то, что должен.
Тест, который требует тяжелой работы по инициализации прежде чем начать собственно тестирование. Иногда случается видеть как сотни строк вызываются для одного единственного теста, создавая при этом множество объектов. Из-за этого «шума» сложно удостовериться что именно тестируется.
Unit-тест, который хотя и правильно тестирует приложение, но растекается на тысячи строк кода и содержит слишком много кейсов. Это может служить признаком того, что тестируемая система представляет из себя антипаттерн Всемогущий Объект (God Object).
Mocking можеть быть очень удобным и правильным. Но случается, что разработчики теряют чувство меры и используют его даже для тех частей системы, которые в принципе должны участвовать в тестировании. В этом случае unit-тест содержит так много mocks, заглушек (stubs) и фейков (fakes), что часть системы остается непротестированной.
Unit-тест, который нарушает инкапсуляцию в попытке достичь 100% покрытия кода (code coverage) и при этом знает слишком много о тестируемой системе. При рефакторинге системы такой тест слишком часто ломается и требует исправлений.
Случай, когда один unit-тест создаёт данные, которые где-то сохраняются, а другой тест их потом переиспользует. Если «генератор данных» будет по какой-то причине вызван позже или пропущен, то тест, использующий его данные, не пройдёт.
Тест, который зависит от чего-то специфичного для данного окружения. В результате тест успешно проходит у конкретного разработчика, но не выполняется у других.
Unit-тест, который проверяет весь результат работы, в то время как на самом деле важна только его малая часть. В результате приходится часто обновлять тест, чтобы отражать изменения в незначительных вещах. Типичен при тестировании веб-приложений.
Тест, который на первый взгляд не делает никакого тестирования из-за отсутствия assertions, но на самом деле дёргает за ниточки системы и полагается на выбрасывание какого-то исключения в случае проблем. Ожидается, что тестовое окружение поймает эту ошибку и отобразит тест как проваленный.
Unit-тест, который тестирует множество второстепенных (и, как правило, простых) мелочей, но не тестирует основное поведение.
Unit-тест, который забивает консоль множеством диагностических сообщений, логов и другой информацией, даже если тест проходит успешно. Иногда является результатом ненужного кода, который не был удалён после отладки теста.
Тест, который ловит исключения и «проглатывает» их, либо заменяя на менее информативное сообщение, либо просто выводя ошибку на консоль, позволяя тесту успешно завершиться.
Тест, который зависит от того, что фактически неупорядоченные данные всегда появляются в одном и том же порядке.
Близкий родственник Местного Героя. Это unit-тест, который требует, чтобы перед запуском были заполнены какие-то данные. Если эти данные отсутствуют, то тест падает, оставляя мало информации о причине проблемы и заставляя разработчика копаться в груде кода для того, чтобы определить какие данные и откуда должны были взяться.
Unit-тест, в котором все кейсы плохо именованы (например, test1, test2, test3). В результате назначение тест-кейса неясно и единственный способ понять, что сломалось — лезть в код теста и молиться, чтобы он оказался понятным.
Кейс, который не относится к unit-тесту, в котором он расположен. Он на самом деле тестирует совершенно другой объект, чаще всего объект, который используется основным тестируемым объектом. Также известен как Дальний Родственник.
Unit-тест, который полагается на особенности определённой операционной системы. Хорошим примером будет тест, который ожидает перевода строки, принятого в Windows и ломающийся, когда выполняется под Linux.
Тест, который был написан для того, чтобы пройти успешно, а не для того, чтобы сначала провалиться (принцип fail first). Побочным эффектом является недостаточно глубокое тестирование и успешное прохождение там, где правильный тест должен упасть.
Вместо того, чтобы написать новый кейс-метод, просто добавляется новый assert к существующему кейсу.
Комбинация нескольких анти-паттернов, в особенности «Зайца» и Гиганта. Такой unit-тест состоит из единственного метода, который тестирует всю функциональность объета. Типичным индикатором проблемы являтся название тестового метода по названию unit-теста и большое количество строк инициализации и assert-ов.
Тест, который из-за общих ресурсов может видеть данные других тестов и может упасть, даже если тестируемая система полностью валидна. Типичным примером может служить использование статических полей для хранения коллекций. Если они не очищаются должным образом, то возможны неожиданные побочные эффекты в других тестах. Также известен как анти-паттерн Незваные Гости.
Unit-тест, который выполняется крайне медленно. Когда разработчик запускает его, то у него появляется достаточно времени, чтобы сходить в туалет или покурить. Или, что может быть ещё хуже, он не станет дожидаться завершения тестирования перед тем, как вечером закоммититься и пойти домой.
От переводчика: восхищения умными мыслями — автору, пинки за перевод — мне. :)
Лжец (The Liar)
Unit-тест, который успешно выполняет все кейсы и выглядит работающим правильно, однако при более детальном рассмотрении обнаруживается, что он на самом деле не тестирует то, что должен.
Чрезмерная Инициализация (Excessive Setup)
Тест, который требует тяжелой работы по инициализации прежде чем начать собственно тестирование. Иногда случается видеть как сотни строк вызываются для одного единственного теста, создавая при этом множество объектов. Из-за этого «шума» сложно удостовериться что именно тестируется.
Гигант (Giant)
Unit-тест, который хотя и правильно тестирует приложение, но растекается на тысячи строк кода и содержит слишком много кейсов. Это может служить признаком того, что тестируемая система представляет из себя антипаттерн Всемогущий Объект (God Object).
Подделка (The Mockery)
Mocking можеть быть очень удобным и правильным. Но случается, что разработчики теряют чувство меры и используют его даже для тех частей системы, которые в принципе должны участвовать в тестировании. В этом случае unit-тест содержит так много mocks, заглушек (stubs) и фейков (fakes), что часть системы остается непротестированной.
Инспектор (The Inspector)
Unit-тест, который нарушает инкапсуляцию в попытке достичь 100% покрытия кода (code coverage) и при этом знает слишком много о тестируемой системе. При рефакторинге системы такой тест слишком часто ломается и требует исправлений.
Щедрые Остатки (Generous Leftovers)
Случай, когда один unit-тест создаёт данные, которые где-то сохраняются, а другой тест их потом переиспользует. Если «генератор данных» будет по какой-то причине вызван позже или пропущен, то тест, использующий его данные, не пройдёт.
Местный Герой (The Local Hero)
Тест, который зависит от чего-то специфичного для данного окружения. В результате тест успешно проходит у конкретного разработчика, но не выполняется у других.
Крохобор (The Nitpicker)
Unit-тест, который проверяет весь результат работы, в то время как на самом деле важна только его малая часть. В результате приходится часто обновлять тест, чтобы отражать изменения в незначительных вещах. Типичен при тестировании веб-приложений.
Тайный Ловец (The Secret Catcher)
Тест, который на первый взгляд не делает никакого тестирования из-за отсутствия assertions, но на самом деле дёргает за ниточки системы и полагается на выбрасывание какого-то исключения в случае проблем. Ожидается, что тестовое окружение поймает эту ошибку и отобразит тест как проваленный.
Уклонист (The Dodger)
Unit-тест, который тестирует множество второстепенных (и, как правило, простых) мелочей, но не тестирует основное поведение.
Крикун (The Loudmouth)
Unit-тест, который забивает консоль множеством диагностических сообщений, логов и другой информацией, даже если тест проходит успешно. Иногда является результатом ненужного кода, который не был удалён после отладки теста.
Жадный Ловец (The Greedy Catcher)
Тест, который ловит исключения и «проглатывает» их, либо заменяя на менее информативное сообщение, либо просто выводя ошибку на консоль, позволяя тесту успешно завершиться.
Любитель Порядка (The Sequencer)
Тест, который зависит от того, что фактически неупорядоченные данные всегда появляются в одном и том же порядке.
Скрытая Зависимость (Hidden Dependency)
Близкий родственник Местного Героя. Это unit-тест, который требует, чтобы перед запуском были заполнены какие-то данные. Если эти данные отсутствуют, то тест падает, оставляя мало информации о причине проблемы и заставляя разработчика копаться в груде кода для того, чтобы определить какие данные и откуда должны были взяться.
Счётчик (The Enumerator)
Unit-тест, в котором все кейсы плохо именованы (например, test1, test2, test3). В результате назначение тест-кейса неясно и единственный способ понять, что сломалось — лезть в код теста и молиться, чтобы он оказался понятным.
Чужак (The Stranger)
Кейс, который не относится к unit-тесту, в котором он расположен. Он на самом деле тестирует совершенно другой объект, чаще всего объект, который используется основным тестируемым объектом. Также известен как Дальний Родственник.
Приверженец ОС (The Operating System Evangelist)
Unit-тест, который полагается на особенности определённой операционной системы. Хорошим примером будет тест, который ожидает перевода строки, принятого в Windows и ломающийся, когда выполняется под Linux.
Успех Любой Ценой (Success Against All Odds)
Тест, который был написан для того, чтобы пройти успешно, а не для того, чтобы сначала провалиться (принцип fail first). Побочным эффектом является недостаточно глубокое тестирование и успешное прохождение там, где правильный тест должен упасть.
«Заяц» (The Free Ride)
Вместо того, чтобы написать новый кейс-метод, просто добавляется новый assert к существующему кейсу.
Избранный (The One)
Комбинация нескольких анти-паттернов, в особенности «Зайца» и Гиганта. Такой unit-тест состоит из единственного метода, который тестирует всю функциональность объета. Типичным индикатором проблемы являтся название тестового метода по названию unit-теста и большое количество строк инициализации и assert-ов.
Подглядыватель (The Peeping Tom)
Тест, который из-за общих ресурсов может видеть данные других тестов и может упасть, даже если тестируемая система полностью валидна. Типичным примером может служить использование статических полей для хранения коллекций. Если они не очищаются должным образом, то возможны неожиданные побочные эффекты в других тестах. Также известен как анти-паттерн Незваные Гости.
Тормоз (The Slow Poke)
Unit-тест, который выполняется крайне медленно. Когда разработчик запускает его, то у него появляется достаточно времени, чтобы сходить в туалет или покурить. Или, что может быть ещё хуже, он не станет дожидаться завершения тестирования перед тем, как вечером закоммититься и пойти домой.
От переводчика: восхищения умными мыслями — автору, пинки за перевод — мне. :)