Этот пост не посвящен разработке через тестирование, передовым методам тестирования или какой-либо передовой концепции тестирования. Вместо этого цель состоит в том, чтобы порекомендовать простое изменение мышления, которое позволит писать тестируемый код без необходимости предварительного написания каких-либо тестов.
Смысл этого в том, чтобы в конечном итоге написать тесты, потому что не каждая команда может позволить себе разработку, основанную на тестировании. Это изменение мышления позволит писать код, который можно будет легко протестировать позже, и который несет в себе некоторые преимущества TDD без предварительного написания тестов.
Но сначала рассказ ...
Уилл только что присоединился к компании, занимающейся онлайн-обучением, в качестве инженера-программиста. Его первой задачей было добавить средний показатель по классу к заданию, выполняемому после сдачи экзамена.
Менеджер по продукту четко объясняет задачу, и Уилл копается в коде. Спросив коллегу, он выясняет, где находится код этой работы, и находит следующее:
Похоже, это достаточно простая работа, Уилл изменяет код, добавляя среднюю статистику:
В восторге от того, что задача завершилась довольно быстро, оставалось только убедиться, что его реализация работает без каких-либо поломок. После такой проверки он мог уверенно фиксировать код.
Повторный разговор со своим коллегой помогает ему понять, как тестировать платформу, поэтому он идет дальше и создает новый класс, создает экзамен, создает двух студентов, входит в систему как каждый и отправляет экзамен. Работа после экзамена выполняется, и, к счастью, результаты казались хорошими.
Однако Уиллу потребовалось полдня, чтобы настроить сквозной тест и создать все эти фиктивные данные. Ему также потребуется около часа, чтобы сделать это снова, если кто-то запросит еще одно изменение в этом фрагменте кода. Ему придется провести сквозной тест с несколькими примерами ввода, чтобы попытаться охватить крайние случаи.
Снижение производительности
В приведенной выше истории Уилл сталкивается с множеством потенциальных потерь производительности, которые влияют на его независимость как разработчика, производительность его коллег, а также его скорость и уверенность при внесении изменений в код. Тесты, вероятно, решат все эти проблемы:
- Наборы тестов действуют как документация (нет сильной зависимости от коллег)
- Выполнение тестов позволит убедиться, что изменение не нарушило предыдущую функциональность, а также убедитесь, что новая функциональность работает должным образом.
- Написание тестов заставит разработчика задуматься о крайних случаях
Но вам, вероятно, не нужно убеждать в полезности тестов. Вышеупомянутые утечки также возникли бы, если бы Уилл был опытным разработчиком в компании.
Поговорим о тестируемости
Наше определение тестируемости будет заключаться в том, насколько легко протестировать определенный фрагмент кода. Рассмотрим эту функцию:
Для введенного изменения Will единственная часть, которая потребует тестирования, - это недавно введенный средний балл (для простоты давайте проигнорируем значение изменения схемы для этой базы данных). Было бы достаточно, если бы Уилл написал тест для max_score
, min_score
и avg_score
, чтобы подтвердить, что его код будет работать.
Проблема в том, как организован код. Трудно проверить наличие зависимостей в базе данных (и, возможно, многих других вещей в производственном коде). Более «тестируемый» код будет следующим:
Если бы мы хотели написать тест для этого кода, мы могли бы просто протестировать calculate_stats
с некоторыми фиктивными данными.
Изменение, необходимое для перехода от непроверяемого к тестируемому в этом примере, довольно просто сделать, чего нет в реальном мире. Этот игрушечный пример служит для иллюстрации того, что простое принятие мышления тестируемого кода приведет к тестируемым функциям, которые мы могли бы легко написать тесты на некоторое время в будущем.
Если бы код, с которым столкнулся Уилл, можно было протестировать, он бы изменил код, а затем написал тест, чтобы подтвердить, что он работает должным образом. Не было бы необходимости настраивать сквозной тест и тратить время на заполнение фиктивных данных внешнего интерфейса, а затем запускать все задание, чтобы проверить, работает ли оно для разных входов.
Вероятно, все было бы иначе, если бы Уилл увидел сложные зависимости во всем коде. Добавление тестов к такому коду требует рефакторинга больших разделов, а новичку это сделать очень сложно, поскольку для этого требуется знание кода. Со временем Уилл, вероятно, привыкнет к непроверяемому стилю кода и низкому уровню продуктивности.
Пример тестируемого кода с добавлением теста:
Это показывает нам, как тестируемый код (даже если мы проигнорируем тест) достиг следующего:
- Документация по коду (извлечение статистики находится в отдельной функции под названием
calculate_stats
) - Легкость написания тестов (поскольку код правильно разделен) вынуждает разработчиков добавлять тесты
- Дает разработчикам уверенность в том, что они могут быстро вносить изменения, создавая и выполняя тесты.
Приведенный выше код также будет результатом применения шаблона разделения проблем, но достигается иначе, просто думая о тестируемости кода. Преимущества такого мышления превышают преимущества обширного знания шаблонов проектирования. Более простые основы приводят к более элегантному и удобному для разработчиков коду.
Таким образом, установка на написание тестируемого кода дает множество преимуществ; бесплатная документация, простота добавления тестов в будущем и повышенная уверенность в фиксации изменений за счет более простого кода - вот лишь некоторые из них. В этом суть разработки через тестирование, без принуждения подхода, ориентированного на тестирование.
Этот пост изначально опубликован на https://softgrade.org. Чтобы получать горячие отпечатки от прессы, рассмотрите возможность подписки в качестве бесплатного члена! Средние сообщения появятся через пару месяцев.