
TDD в геймдеве или «кроличий ад»

Разработка через тестирование
В ходе очередного ревью толстого Pull Request'а наткнулся на Unit Test'ы с некорректным именованием тест-кейсов. Обсуждение формулировок в тест-кейсах получилось похожим на разговор Янычара и Легкоступова в к/ф "72 метра" ("если б мне в школе так доходчиво..."). В разговоре прозвучала мысль, что в рускоязычных ресурсах трудно найти толковый гайд именно по текстовым формулировкам. Решил искать самолично на русском (обычно я пользуюсь только англоязычными источниками). На хабре нашел несколько мануалов про юнит-тесты, но все они обходят стороной детали формулировок в тест-кейсах. Под катом моя попытка восполнить данный пробел.
Данная статья будет интересна как тестировщикам, так и разработчикам, но рассчитана в большей степени на автоматизаторов, которые столкнулись с проблемой настройки GitLab CI/CD для проведения интеграционного тестирования в условиях недостаточности инфраструктурных ресурсов и/или отсутствия платформы оркестрации контейнеров. Я расскажу, как настроить развертывание тестируемых окружений при помощи docker compose на одном единственном GitLab shell раннере и так, чтобы при развертывании нескольких окружений запускаемые сервисы друг другу не мешали.
Это продолжение истории, которая началась здесь, а продолжалась здесь и здесь.
В прошлой части я написал интеграционный тест, демонстрирующий процесс инициализации и выполнения полного набора обработчиков, извлекающих данные из базы. Но поскольку от написания этого теста, до его запуска, может пройти слишком длительное время, необходимое для кодирования не только обработчика, но и правил настройки для всех необходимых запросов к базе, то сегодня я решил реализовать его модульную версию, расчитанную на конфигурирование и запуск всего одного обработчика. Выглядит это тест вот как:
В предыдущей части я остановился на том, что разрабатываемая мной команда реализует поведение, которое можно описать вот таким тестом:
it('execute should return promise', () => {
request.configure(options);
request.execute().then((result) => {
expect(result.Id).toEqual(1);
expect(result.Name).toEqual('Jack');
});
});
В настоящий момент занимаюсь реализацией взаимодействия с поставщиком KYC услуг. Как обычно ничего космического. Нужно просто выбрать из своей базы данных некий достаточно объемный комплект экземпляров различных записей, выгрузить их поставщику услуг и попросить поставщика записи эти проверить.
Начальная стадия обработки содержит десяток идентичных операций с отправкой запросов на извлечение данных некоего конкретного пользователя из различных таблиц базы. Есть предположение что при этом достаточно большую часть кода можно будет использовать повторно в виде абстракции Request
. Попробую предположить как этим можно было бы пользоваться. Напишу первый тест:
Spock предоставляет 3 мощных (но разных по сути) инструмента, упрощающих написание тестов: Mock, Stub и Spy.
Довольно часто коду, который нужно протестировать, требуется взаимодействовать с внешними модулями, называющимися зависимостями (в оригинальной статье используется термин collaborators, который не очень распространён в русскоязычной среде).
Модульные тесты чаще всего разрабатываются для тестирования одного изолированного класса при помощи различных вариантов моков: Mock, Stub и Spy. Так тесты будут надёжнее и будут реже ломаться по мере того, как код зависимостей эволюционирует.
Такие изолированные тесты менее подвержены проблемам при изменении внутренних деталей реализации зависимостей.
От переводчика: каждый раз, когда я использую Spock Framework для написания тестов, я чувствую, что могу ошибиться при выборе способа подмены зависимостей. В этой статье есть максимально краткая шпаргалка по выбору механизма для создания моков.
Вторая статья из цикла "Test-Driven Development приложений на Spring Boot" и в этот раз я буду говорить про тестирование доступа к базе данных, важного аспекта интеграционного тестирования. Я расскажу как через тесты определять интерфейс будущего сервиса для доступа к данным, как использовать встраиваемые in-memory базы для тестирования, работать с транзакциями и загружать тестовые данные в базу.
Несмотря на то, что технологии модульного тестирования существуют уже 30 лет (в 1989 году Кент Бек написал статью “Simple Smalltalk Testing: With Patterns”), тем не менее не все программисты владеют этой технологией и не все компании сделали автоматическое тестирование частью своей корпоративной культуры. Даже несмотря на очевидные преимущества автоматического тестирования, все равно поведенческое сопротивление достаточно сильное. Кто пробовал внедрять автоматические тесты, тот знает, что всегда найдется какая-то причина, почему это не удалось сделать.
Из моего личного опыта внедрения методов надежного программирования в своей компании, в компаниях, которые я консультировал, общения на конференциях, а также из публично доступных источников, я сформулировал типичные возражения и сопротивления, которые препятствуют внедрению культуры автоматического тестирования.
Все возражения я сгруппировал в пирамиду надежного программирования, которая включает четыре уровня: