Этот пост не посвящен разработке через тестирование, передовым методам тестирования или какой-либо передовой концепции тестирования. Вместо этого цель состоит в том, чтобы порекомендовать простое изменение мышления, которое позволит писать тестируемый код без необходимости предварительного написания каких-либо тестов.

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

Но сначала рассказ ...

Уилл только что присоединился к компании, занимающейся онлайн-обучением, в качестве инженера-программиста. Его первой задачей было добавить средний показатель по классу к заданию, выполняемому после сдачи экзамена.

Менеджер по продукту четко объясняет задачу, и Уилл копается в коде. Спросив коллегу, он выясняет, где находится код этой работы, и находит следующее:

Похоже, это достаточно простая работа, Уилл изменяет код, добавляя среднюю статистику:

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

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

Однако Уиллу потребовалось полдня, чтобы настроить сквозной тест и создать все эти фиктивные данные. Ему также потребуется около часа, чтобы сделать это снова, если кто-то запросит еще одно изменение в этом фрагменте кода. Ему придется провести сквозной тест с несколькими примерами ввода, чтобы попытаться охватить крайние случаи.

Снижение производительности

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

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

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

Поговорим о тестируемости

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

Для введенного изменения Will единственная часть, которая потребует тестирования, - это недавно введенный средний балл (для простоты давайте проигнорируем значение изменения схемы для этой базы данных). Было бы достаточно, если бы Уилл написал тест для max_score, min_score и avg_score, чтобы подтвердить, что его код будет работать.

Проблема в том, как организован код. Трудно проверить наличие зависимостей в базе данных (и, возможно, многих других вещей в производственном коде). Более «тестируемый» код будет следующим:

Если бы мы хотели написать тест для этого кода, мы могли бы просто протестировать calculate_stats с некоторыми фиктивными данными.

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

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

Вероятно, все было бы иначе, если бы Уилл увидел сложные зависимости во всем коде. Добавление тестов к такому коду требует рефакторинга больших разделов, а новичку это сделать очень сложно, поскольку для этого требуется знание кода. Со временем Уилл, вероятно, привыкнет к непроверяемому стилю кода и низкому уровню продуктивности.

Пример тестируемого кода с добавлением теста:

Это показывает нам, как тестируемый код (даже если мы проигнорируем тест) достиг следующего:

  • Документация по коду (извлечение статистики находится в отдельной функции под названием calculate_stats)
  • Легкость написания тестов (поскольку код правильно разделен) вынуждает разработчиков добавлять тесты
  • Дает разработчикам уверенность в том, что они могут быстро вносить изменения, создавая и выполняя тесты.

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

Таким образом, установка на написание тестируемого кода дает множество преимуществ; бесплатная документация, простота добавления тестов в будущем и повышенная уверенность в фиксации изменений за счет более простого кода - вот лишь некоторые из них. В этом суть разработки через тестирование, без принуждения подхода, ориентированного на тестирование.

Этот пост изначально опубликован на https://softgrade.org. Чтобы получать горячие отпечатки от прессы, рассмотрите возможность подписки в качестве бесплатного члена! Средние сообщения появятся через пару месяцев.