Хрупкие тесты в CI — проблема тестировщика
Автоматизация должна ускорять тестирование, делать тесты детерминистичными (определенными) и надежными. Но так получается не всегда, вопреки планам, автотесты, особенно в инструментальном тестировании (instrumented testing) Android-приложений, часто становятся нестабильными (flaky).
Причины нестабильности тестов коротко (здесь подробнее):
- Ошибки синхронизации между тестами и AUT-приложением
- Вызовы между сервисами падают
- Неупорядоченные тестовые данные
- Тестовое окружение намного хуже по качеству чем прод-окружение
- Test flow, слишком дотошно настроенный на нужный уровень валидации
- 3rd-party-зависимости
Нестабильность автотестов в CI — одна из самых больших проблем в CI-процессах. Тесты не проходят, следовательно, замедляется темп разработки.
Один из возможных способов борьбы с нестабильностью тестов — “изолировать” приложение от всех зависимостей. Это удобно делать, применяя моки — “макеты” (иногда еще называют “каркасами”; здесь подробно о моках).
Как помнишь, пирамида тестирования выглядит так:
Когда применяют моки?
- Mocking-фреймворк дает нужный уровень определенности, изолированности и стабильности.
- Команды работают каждая в своем темпе, не отвлекаясь на не очень существенные задачи.
- Ошибки симулируются и сразу отрабатываются; создаются negative user journeys. И для этого не надо менять настройки окружения, или переводить приложение в другое состояние, мешая другим командам.
- Изолированность окружения — нет таких высоких требований к скорости сети
Моки — практический пример
Возьмем в пример Wiremock. Симулируем зависимости на сервере.
- Добавляем Wiremock-зависимости в gradle для Android-теста
- Встраиваем Wiremock в тесты
Wiremock работает по JUnit-правилам, что касается жизненного цикла сервера и задач setUp/teardown.
Запуск и остановка тест-кейса Wiremock — добавляем правило в класс теста:
! Внимание, сейчас надо проверить, что 8080 порт уже нигде не используется
- Перехватываем запрос и меняем URL на localhost
Создаем Interceptor для перехвата HTTPS и замены URL на http://127.0.0.1:8080.
Добавляем перехватчик в Network-модуль:
- Симулируем ответы. Их варианты:
4.1. Ответ с ошибкой. Ответ со статусом 400, возвращаемый когда URL = /mocked/url, а тип запроса — GET. Тело ответа будет jsonBody. Данные ошибки:
4.2. Пустое тело ответа. Ответ со статусом 429, возвращаемый когда URL = /mocked/url, тип запроса — POST, и тело ответа пустое:
4.3. Успешный ответ. Возвращается статус 200, когда URL = /mocked/url, а тип запроса — PUT.
!RemoveStub удаляет stub mapping после каждого запуска теста, это финальные teardown-операции тестового класса:
- Тесты с mock-симуляцией ответа
5.1. Тест проверяет наличие сообщения об ошибке в UI при получении статуса 400 и данными ошибки в JSON-теле:
5.2. Тест проверяет отсутствие ошибки в UI при получении статуса 200:
!Как и в предыдущем примере, применяется teardown для стирания stub mapping после каждого теста:
Вот так моки в Wiremock упрощают QA-процессы. Главное пользоваться этой полезной штукой с умом, при необходимости, соблюдая KISS-принцип (Keep It Simple), иначе получится то что называется over-engineered.