Некоторые Тесты Важны, Другие Не Нужны

Зачем писать тесты? Когда их писать? Когда не стоит их писать? Хабр был и остается неравнодушным к этим и другим вечным вопросам, и не один хабровский житель провел бессонную ночь, придумывая на них убедительные ответы (чтобы оппоненты наконец замолчали).

Я решил победить врага не умением, а количеством, и поэтому вот 19 причин, почему писать тесты все же стоит (в хронологическом порядке).

Итак, тесты

  1. Они помогают разрабатывать API создаваемого класса.

    Вместо того, чтобы изобретать интерфейс класса, вы изобретаете его во время написания теста.

  2. Помогите разработать архитектуру приложения.

    Как минимум, это низкоуровневая архитектура — способ взаимодействия классов друг с другом.

    Опыт показывает, что такая архитектура, как правило, оказывается наиболее гибкой и экономически эффективной.

  3. Они проверяют, работает ли определенный фрагмент кода прямо сейчас.

  4. Они проверяют, будет ли работать определенный фрагмент кода после внесенных изменений.

  5. Документируйте функциональность отдельных классов.

  6. Документируйте поведение системы (с точки зрения пользователя).

  7. Я пока ни о чем другом не думал, может кто подскажет?
Понятно, что тест от теста отличается, и не все тесты могут принести такую большую пользу.

Давайте попробуем разобраться.



Пишем ли мы тесты до или после кода?

Здесь все более-менее понятно.

Когда мы пишем тест после кода, мы теряем бесценную возможность позволить тестам помочь нам написать код. На мой взгляд, это самая большая ценность тестов.

Однако это и самое сложное для понимания, потому что здесь нужно заткнуть своего внутреннего Архитектора и позволить испытаниям взять верх.

Второй важный момент: как говорят многие опытные люди, тесты, отложенные на потом, в большинстве случаев остаются ненаписанными.

Вердикт: Мы пишем тесты после кода только в тех случаях, когда нам нужно быстро заработать, не особо беспокоясь о последствиях.



Какой фрагмент кода мне следует протестировать?

Здесь нет «или-или», а есть непрерывный спектр: от однострочного метода до целого сценария, включающего последовательность действий пользователя.

Первая крайность называется модульным тестированием, вторая — приемочным тестированием, а где-то посередине находятся интеграция (кто-нибудь знает слово получше?) и функциональное тестирование, каждое из которых имеет разное значение для каждого из этих терминов.

Среди людей модульное тестирование гораздо более популярно, и когда речь идет о разработке через тестирование, по умолчанию подразумеваются модульные тесты.

Во-первых, их гораздо проще писать — в том смысле, что для этого требуется меньше кода, а во-вторых, это связано с тем, что разработчики любят писать тесты разработки (подробнее о них позже).

Давайте теперь посмотрим, как эти и другие тесты связаны с нашим списком хороших вещей: Модульные тесты:

  1. Они помогают разрабатывать API создаваемого класса.

  2. Помогите разработать архитектуру приложения.

  3. Они проверяют, работает ли определенный фрагмент кода прямо сейчас.

  4. Они проверяют, будет ли работать определенный фрагмент кода после внесенных изменений.

  5. Документируйте функциональность отдельных классов.

Как видите преимуществ огромное количество, не хватает только одного (самого главного) — проверки корректности работы всей системы или хотя бы какой-то значимой ее части.

Подобно тому, как набор здоровых нейронов может породить больной мозг, набор правильно функционирующих классов не гарантирует нормальной работы системы.

Даже если мы протестируем взаимодействие классов друг с другом, модульный тест даст нам лишь гарантию того, что данный класс посылает своим соседям какие-то определенные сигналы.

Но нужны ли эти сигналы всеобщей гармонии, модульный тест нам не скажет. И тут на помощь приходит интеграционный тест. Писать сложнее, потому что.

необходимо создать контекст, состоящий из нескольких частей; его сложнее поддерживать, т.к.

при провале теста нужно много времени, чтобы понять, какой кусок кода исправить, чтобы и тест можно было поднять, и остальные не провалились.

Но теперь мы можем быть уверены, что наша система ведет себя корректно в определенных конкретных условиях.

Помогает ли интеграционный тест при разработке приложения? Сам по себе нет, он проверяет огромный кусок кода, и этот кусок сам по себе может быть организован совершенно некрасиво.

Но если мы начнем с интеграционного теста, а затем, в процессе рефакторинга, добавим модульные тесты в отдельные участки кода, это принесет поистине бесчисленные преимущества.



Что нам вообще следует протестировать?

На этот вопрос большинство разработчиков дадут очевидный и, прямо скажем, очень глупый, простодушный ответ: мы будем тестировать классы и их методы.

Сказано - сделано.

Для каждого класса, который придумывает наш гениальный мозг, мы создаем тестовый класс.

Например, если у нас есть MyClass, мы просто создаем класс MyClassTester. Далее, поскольку мы уже точно знаем, что у нас есть метод DoSomething, мы создаем метод TestDoSomething. И, что самое интересное, после этого мы спокойно спим по ночам.

Говорят, в Visual Studio есть даже команда, которая всё это генерирует автоматически.

Если мы спросим заказчика или даже, например, тестировщика, то получим еще один вполне очевидный ответ: мы протестируем поведение системы.

У нас есть спецификация, или есть пользовательские истории, мы берём и переписываем их в исполняемый вид. А это уже работа для мозга: именно в процессе написания таких тестов начинается этот священный для многих присутствующих процесс, называемый Test Driven Design. Первый тип тестов иногда называют тестами разработки, а второй — пользовательскими тестами.

Существует тенденция путать разделение модульных и интеграционных тестов.

Я со всей ответственностью заявляю, что это не так.

Иногда вы можете превратить тесты разработчиков в пользовательские, просто переименовав и переставив методы тестирования.

Типичный пример — поиск.

У нас может быть один класс, отвечающий за выбор данных по нескольким параметрам.

В случае с тестами разработки у нас есть один тестовый класс с кучей тестов.

Разобьем на классы по сценариям: поиск по названию, по дате и т.д. Мы получили пользовательские тесты.

Как их написать и организовать не совсем очевидно, но польза от них огромна.

Помимо следующего, мы получаем систему, в которой нет избыточного тестирования.

Это означает, что мы можем менять реализацию функциональности, не опасаясь, что некоторые тесты провалятся (а именно те, которые тестируют не функции, а способ их реализации).

Итак, тесты разработчиков:

  • Они не помогают разрабатывать API создаваемого класса.

    Ведь все классы и методы мы уже придумали.

  • Они помогают развивать архитектуру отдельных методов и взаимодействие с другими классами.

  • Они проверяют, работает ли определенный фрагмент кода прямо сейчас.

  • Они проверяют, будет ли работать определенный фрагмент кода после внесенных изменений.

    Однако неясно, работает ли это так, как нужно системе с новыми требованиями.

  • Документируйте функциональность отдельных классов.

    Однако чаще всего, чтобы разобраться в этом, нужно покопаться в коде метода.

  • Они не документируют поведение системы.

Пользовательские тесты:
  • Они помогают разрабатывать API создаваемого класса.

  • Помогите разработать архитектуру приложения.

  • Они проверяют, работает ли определенный фрагмент кода прямо сейчас.

  • Они проверяют, будет ли работать определенный фрагмент кода после внесенных изменений.

  • Они не документируют функциональность отдельных классов.

  • Документируйте поведение системы.

    При этом можно настолько грамотно организовать эти тесты, что поведение системы будет понятно по названию теста (не нужно углубляться в код).

    Существуют даже системы, которые автоматически генерируют документацию на основе названий тестов.

То есть, если я хочу понять, что делает этот класс, мне придется посмотреть код самого класса.

Чтобы понять пример его использования, я рассмотрю тестовый код разработчика.

Но чтобы понять, как система делает ту или иную полезную вещь, я посмотрю код пользовательского теста.

Вердикт: Хотя я всегда категорически игнорировал тесты для разработчиков, я готов признать, что они могут быть полезны в определенных ситуациях.

Но я все равно выбираю индивидуальные.



Последние мысли

Ничего окончательного я не придумал, поэтому оставляю все как есть.

Теги: #tdd #tdd

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.