Зачем писать тесты? Когда их писать? Когда не стоит их писать? Хабр был и остается неравнодушным к этим и другим вечным вопросам, и не один хабровский житель провел бессонную ночь, придумывая на них убедительные ответы (чтобы оппоненты наконец замолчали).
Я решил победить врага не умением, а количеством, и поэтому вот 19 причин, почему писать тесты все же стоит (в хронологическом порядке).
Итак, тесты
- Они помогают разрабатывать API создаваемого класса.
Вместо того, чтобы изобретать интерфейс класса, вы изобретаете его во время написания теста.
- Помогите разработать архитектуру приложения.
Как минимум, это низкоуровневая архитектура — способ взаимодействия классов друг с другом.
Опыт показывает, что такая архитектура, как правило, оказывается наиболее гибкой и экономически эффективной.
- Они проверяют, работает ли определенный фрагмент кода прямо сейчас.
- Они проверяют, будет ли работать определенный фрагмент кода после внесенных изменений.
- Документируйте функциональность отдельных классов.
- Документируйте поведение системы (с точки зрения пользователя).
- Я пока ни о чем другом не думал, может кто подскажет?
Давайте попробуем разобраться.
Пишем ли мы тесты до или после кода?
Здесь все более-менее понятно.Когда мы пишем тест после кода, мы теряем бесценную возможность позволить тестам помочь нам написать код. На мой взгляд, это самая большая ценность тестов.
Однако это и самое сложное для понимания, потому что здесь нужно заткнуть своего внутреннего Архитектора и позволить испытаниям взять верх.
Второй важный момент: как говорят многие опытные люди, тесты, отложенные на потом, в большинстве случаев остаются ненаписанными.
Вердикт: Мы пишем тесты после кода только в тех случаях, когда нам нужно быстро заработать, не особо беспокоясь о последствиях.
Какой фрагмент кода мне следует протестировать?
Здесь нет «или-или», а есть непрерывный спектр: от однострочного метода до целого сценария, включающего последовательность действий пользователя.Первая крайность называется модульным тестированием, вторая — приемочным тестированием, а где-то посередине находятся интеграция (кто-нибудь знает слово получше?) и функциональное тестирование, каждое из которых имеет разное значение для каждого из этих терминов.
Среди людей модульное тестирование гораздо более популярно, и когда речь идет о разработке через тестирование, по умолчанию подразумеваются модульные тесты.
Во-первых, их гораздо проще писать — в том смысле, что для этого требуется меньше кода, а во-вторых, это связано с тем, что разработчики любят писать тесты разработки (подробнее о них позже).
Давайте теперь посмотрим, как эти и другие тесты связаны с нашим списком хороших вещей: Модульные тесты:
- Они помогают разрабатывать API создаваемого класса.
- Помогите разработать архитектуру приложения.
- Они проверяют, работает ли определенный фрагмент кода прямо сейчас.
- Они проверяют, будет ли работать определенный фрагмент кода после внесенных изменений.
- Документируйте функциональность отдельных классов.
Подобно тому, как набор здоровых нейронов может породить больной мозг, набор правильно функционирующих классов не гарантирует нормальной работы системы.
Даже если мы протестируем взаимодействие классов друг с другом, модульный тест даст нам лишь гарантию того, что данный класс посылает своим соседям какие-то определенные сигналы.
Но нужны ли эти сигналы всеобщей гармонии, модульный тест нам не скажет. И тут на помощь приходит интеграционный тест. Писать сложнее, потому что.
необходимо создать контекст, состоящий из нескольких частей; его сложнее поддерживать, т.к.
при провале теста нужно много времени, чтобы понять, какой кусок кода исправить, чтобы и тест можно было поднять, и остальные не провалились.
Но теперь мы можем быть уверены, что наша система ведет себя корректно в определенных конкретных условиях.
Помогает ли интеграционный тест при разработке приложения? Сам по себе нет, он проверяет огромный кусок кода, и этот кусок сам по себе может быть организован совершенно некрасиво.
Но если мы начнем с интеграционного теста, а затем, в процессе рефакторинга, добавим модульные тесты в отдельные участки кода, это принесет поистине бесчисленные преимущества.
Что нам вообще следует протестировать?
На этот вопрос большинство разработчиков дадут очевидный и, прямо скажем, очень глупый, простодушный ответ: мы будем тестировать классы и их методы.Сказано - сделано.
Для каждого класса, который придумывает наш гениальный мозг, мы создаем тестовый класс.
Например, если у нас есть MyClass, мы просто создаем класс MyClassTester. Далее, поскольку мы уже точно знаем, что у нас есть метод DoSomething, мы создаем метод TestDoSomething. И, что самое интересное, после этого мы спокойно спим по ночам.
Говорят, в Visual Studio есть даже команда, которая всё это генерирует автоматически.
Если мы спросим заказчика или даже, например, тестировщика, то получим еще один вполне очевидный ответ: мы протестируем поведение системы.
У нас есть спецификация, или есть пользовательские истории, мы берём и переписываем их в исполняемый вид. А это уже работа для мозга: именно в процессе написания таких тестов начинается этот священный для многих присутствующих процесс, называемый Test Driven Design. Первый тип тестов иногда называют тестами разработки, а второй — пользовательскими тестами.
Существует тенденция путать разделение модульных и интеграционных тестов.
Я со всей ответственностью заявляю, что это не так.
Иногда вы можете превратить тесты разработчиков в пользовательские, просто переименовав и переставив методы тестирования.
Типичный пример — поиск.
У нас может быть один класс, отвечающий за выбор данных по нескольким параметрам.
В случае с тестами разработки у нас есть один тестовый класс с кучей тестов.
Разобьем на классы по сценариям: поиск по названию, по дате и т.д. Мы получили пользовательские тесты.
Как их написать и организовать не совсем очевидно, но польза от них огромна.
Помимо следующего, мы получаем систему, в которой нет избыточного тестирования.
Это означает, что мы можем менять реализацию функциональности, не опасаясь, что некоторые тесты провалятся (а именно те, которые тестируют не функции, а способ их реализации).
Итак, тесты разработчиков:
- Они не помогают разрабатывать API создаваемого класса.
Ведь все классы и методы мы уже придумали.
- Они помогают развивать архитектуру отдельных методов и взаимодействие с другими классами.
- Они проверяют, работает ли определенный фрагмент кода прямо сейчас.
- Они проверяют, будет ли работать определенный фрагмент кода после внесенных изменений.
Однако неясно, работает ли это так, как нужно системе с новыми требованиями.
- Документируйте функциональность отдельных классов.
Однако чаще всего, чтобы разобраться в этом, нужно покопаться в коде метода.
- Они не документируют поведение системы.
- Они помогают разрабатывать API создаваемого класса.
- Помогите разработать архитектуру приложения.
- Они проверяют, работает ли определенный фрагмент кода прямо сейчас.
- Они проверяют, будет ли работать определенный фрагмент кода после внесенных изменений.
- Они не документируют функциональность отдельных классов.
- Документируйте поведение системы.
При этом можно настолько грамотно организовать эти тесты, что поведение системы будет понятно по названию теста (не нужно углубляться в код).
Существуют даже системы, которые автоматически генерируют документацию на основе названий тестов.
Чтобы понять пример его использования, я рассмотрю тестовый код разработчика.
Но чтобы понять, как система делает ту или иную полезную вещь, я посмотрю код пользовательского теста.
Вердикт: Хотя я всегда категорически игнорировал тесты для разработчиков, я готов признать, что они могут быть полезны в определенных ситуациях.
Но я все равно выбираю индивидуальные.
Последние мысли
Ничего окончательного я не придумал, поэтому оставляю все как есть.Теги: #tdd #tdd
-
10 Способов Провалить Собеседование
19 Oct, 24 -
Неделя Экспресс-Школ Imagine Cup
19 Oct, 24 -
Полезные Утилиты
19 Oct, 24 -
Цена На Cms Melbis Shop Снизилась Вдвое
19 Oct, 24