Ускоряйте тесты, говорили они.
И вот уже прошло почти полгода, как мы переписали свои старые необтёсанные, долгие и не стабильные функциональные тесты и перешли на быстрые, ни от чего не зависящие компонентные. Поэтому, пора делиться :)
Для тех кто не знает, компонентные тесты — это тесты которые полностью изолированы от глобального окружения и позволяют проверить те или иные кейсы, которые unit тест не смог бы охватить.
Полгода назад релиз какой-либо фичи, бывало занимало больше часа с учетом того, что код уже давно на мастере и полностью проверен, но мастер ветка никак не может добиться зеленой сборки в bamboo и тогда, встал вопрос, как дальше жить?
Ведь, в этом случае от тестов вреда больше, чем пользы, но избавляться вовсе и “забить” на тесты, далеко не лучший вариант :) Тогда, тимлидом была организована небольшая микро-команда в лице:
Быстро скооперировавшись, мы разделили себе задачи и одной из них было — настроить среду для написания компонентных тестов. Таким образом и началось моё путешествие.
На момент начала разработки, у нас было 140+ функциональных тестов, которые запускались в несколько потоков под разные окружения (Frontend, Mobile, Backend) и проходили они ~5-7 минут; также нередко приходилось их перезапускать, чтобы добиться зеленой сборки. А падали эти тесты больше не из-за нового написанного кода, а из-за проблем в окружении, то есть, где-то API не ответил, где-то микросервис тестируемый упал и т.д. Это останавливало работу всего отдела, так как сборки запускались почти каждые 5-10 минут: кто-то пересобирал, кто-то пушил новый код…
После первой половины недели, пришли к тому, что будем “мокать” наше API и сторонние сервисы, что дало бы нам полностью изолированную среду тестирования. Но вставал вопрос: писать что-то своё или… Так вот, на этом “или” всё и завершилось — недолгими поисками, на своём пути я встретил — небольшую наработку в виде Mock сервера “http-api-mock”.
http-api-mock — легкий и не требующий установки mock сервер, написанный на языке Go с неплохой документацией.
Спустя сотни попыток запустить, а также вообще вникнуть в тему моков, мне всё же удалось переписать 1 функциональный тест, который создавал новое объявление на сайте и, пройдя все круги ада, убеждался, в том, что заголовок на странице соответствует заголовку в теле объекта.
Представьте себе, заработало! Переписанный тест оказался в 3 раза быстрее, чем предыдущий, так как здесь мы не проверяли создание, модерацию, а сразу же отдали из мока нужный объект объявления и выиграли на этом. Эта маленькая победа стала хорошим стимулом для дальнейшего развития этой темы, тем самым, спустя еще неделю, у нас был новый suite в codeception с названием “component”, который имел уже базовый helper класс для работы с нашим Mock сервером и запускался на тот момент у меня на песочнице.
Базовый класс-помощник умеет создавать объявление в виде json-файла в директории конфигов нашего mock-сервера, отдавать нужное объявление по id и т.д. Почти API.
Остальная магия ждала нас дальше — теперь оставалось настроить план сборок в bamboo. Чтобы наши тесты теперь проходили уже через наш CI&CD.
В этом нам помог админ — с настройкой запуска всех этих тестов в docker, что позволяло под каждую сборку поднимать свой контейнер с Mock сервером и в полностью изолированной среде запускать наши тесты, не деплоя наш код на какой-либо тестовый бэкенд, что также не могло не ускорить прохождение наших тестов.
Для работы всей этой магии, нам пришлось добавить новые конфиг-файлы с новым адресом API и внешних сервисов, а также поднять копию базы mysql, ну и еще создать в build-плане новый таск с запуском нашего mock-сервера.
Теперь для нашего кода есть совершенно новый API, который независимо от любых проблем с окружением отдаст нам, то что мы хотим.
Время шло, тесты переписывались и вот 140 функциональных тестов превратились в 103 компонентных теста, которые проходят параллельно за ~30 секунд.
Очень шустрые. За счёт того, что они полностью независимы от тестового окружения им не приходится ходить за данными куда-то далеко.
Стабильные. Тестам не приходится заботиться, не упал ли там наш API или любой другой сервис, поэтому мы всегда уверены, что результат к нам придёт.
Легко писать. Собственно, в процессе переписывания, многое решалось копированием кода из старого функционального теста в новый компонентный и подготовки для него endpoint-ов в Mock сервере.
Поддержка. Теперь каждый разработчик, который внёс изменения в возвращаемый ответ для определенного endpoint-а в API, также идёт в репозиторий с конфигами для mock-сервера и правит ответ там.
Куча файлов. Данные с конфигами решили хранить в виде файлов, то есть каждый ответ для endpoint-а лежит как файл и где-то может затеряться.
Тесты
Было: 140+ функциональных тестов = 5-7 минут.
Стало: 103 компонентных тестов = ~30 секунд.
Стабильность сборок
Было: Каждая третья сборка падала из-за каких-либо проблем.
Стало: Падает только когда разработчик поломал логику какого-то метода.
В дальнейших планах у нас переписывание acceptance (gui) тестов — также запускать их внутри контейнера и изолировать от остального окружения.
И вот уже прошло почти полгода, как мы переписали свои старые необтёсанные, долгие и не стабильные функциональные тесты и перешли на быстрые, ни от чего не зависящие компонентные. Поэтому, пора делиться :)
Для тех кто не знает, компонентные тесты — это тесты которые полностью изолированы от глобального окружения и позволяют проверить те или иные кейсы, которые unit тест не смог бы охватить.
Полгода назад релиз какой-либо фичи, бывало занимало больше часа с учетом того, что код уже давно на мастере и полностью проверен, но мастер ветка никак не может добиться зеленой сборки в bamboo и тогда, встал вопрос, как дальше жить?
Ведь, в этом случае от тестов вреда больше, чем пользы, но избавляться вовсе и “забить” на тесты, далеко не лучший вариант :) Тогда, тимлидом была организована небольшая микро-команда в лице:
- Тимлида
- Backend разработчика
- QA инженера
- Админа
Быстро скооперировавшись, мы разделили себе задачи и одной из них было — настроить среду для написания компонентных тестов. Таким образом и началось моё путешествие.
На момент начала разработки, у нас было 140+ функциональных тестов, которые запускались в несколько потоков под разные окружения (Frontend, Mobile, Backend) и проходили они ~5-7 минут; также нередко приходилось их перезапускать, чтобы добиться зеленой сборки. А падали эти тесты больше не из-за нового написанного кода, а из-за проблем в окружении, то есть, где-то API не ответил, где-то микросервис тестируемый упал и т.д. Это останавливало работу всего отдела, так как сборки запускались почти каждые 5-10 минут: кто-то пересобирал, кто-то пушил новый код…
После первой половины недели, пришли к тому, что будем “мокать” наше API и сторонние сервисы, что дало бы нам полностью изолированную среду тестирования. Но вставал вопрос: писать что-то своё или… Так вот, на этом “или” всё и завершилось — недолгими поисками, на своём пути я встретил — небольшую наработку в виде Mock сервера “http-api-mock”.
http-api-mock — легкий и не требующий установки mock сервер, написанный на языке Go с неплохой документацией.
Спустя сотни попыток запустить, а также вообще вникнуть в тему моков, мне всё же удалось переписать 1 функциональный тест, который создавал новое объявление на сайте и, пройдя все круги ада, убеждался, в том, что заголовок на странице соответствует заголовку в теле объекта.
Представьте себе, заработало! Переписанный тест оказался в 3 раза быстрее, чем предыдущий, так как здесь мы не проверяли создание, модерацию, а сразу же отдали из мока нужный объект объявления и выиграли на этом. Эта маленькая победа стала хорошим стимулом для дальнейшего развития этой темы, тем самым, спустя еще неделю, у нас был новый suite в codeception с названием “component”, который имел уже базовый helper класс для работы с нашим Mock сервером и запускался на тот момент у меня на песочнице.
Базовый класс-помощник умеет создавать объявление в виде json-файла в директории конфигов нашего mock-сервера, отдавать нужное объявление по id и т.д. Почти API.
Остальная магия ждала нас дальше — теперь оставалось настроить план сборок в bamboo. Чтобы наши тесты теперь проходили уже через наш CI&CD.
В этом нам помог админ — с настройкой запуска всех этих тестов в docker, что позволяло под каждую сборку поднимать свой контейнер с Mock сервером и в полностью изолированной среде запускать наши тесты, не деплоя наш код на какой-либо тестовый бэкенд, что также не могло не ускорить прохождение наших тестов.
Для работы всей этой магии, нам пришлось добавить новые конфиг-файлы с новым адресом API и внешних сервисов, а также поднять копию базы mysql, ну и еще создать в build-плане новый таск с запуском нашего mock-сервера.
# Delete/Create network
docker network rm mock-kolesa-net;
docker network create --subnet=IP_ADDR/24 --gateway IP_ADDR_GATEWAY mock-kolesa-net;
# Docker run http-mock-kolesa
docker run \
--rm \
--name http-mock-kolesa -d \
-v ${CONFIG}/config/:/config \
-v ${CONFIG}/data/:/data \
--user $(id -u):$(id -g) \
--net mock-kolesa-net \
--ip IP_ADDR\
local-docker-hub.kolesa-domain.org:7979/build/http-mock-kolesa;
Теперь для нашего кода есть совершенно новый API, который независимо от любых проблем с окружением отдаст нам, то что мы хотим.
Время шло, тесты переписывались и вот 140 функциональных тестов превратились в 103 компонентных теста, которые проходят параллельно за ~30 секунд.
Из плюсов
Очень шустрые. За счёт того, что они полностью независимы от тестового окружения им не приходится ходить за данными куда-то далеко.
Стабильные. Тестам не приходится заботиться, не упал ли там наш API или любой другой сервис, поэтому мы всегда уверены, что результат к нам придёт.
Легко писать. Собственно, в процессе переписывания, многое решалось копированием кода из старого функционального теста в новый компонентный и подготовки для него endpoint-ов в Mock сервере.
Из минусов
Поддержка. Теперь каждый разработчик, который внёс изменения в возвращаемый ответ для определенного endpoint-а в API, также идёт в репозиторий с конфигами для mock-сервера и правит ответ там.
Куча файлов. Данные с конфигами решили хранить в виде файлов, то есть каждый ответ для endpoint-а лежит как файл и где-то может затеряться.
Результаты:
Тесты
Было: 140+ функциональных тестов = 5-7 минут.
Стало: 103 компонентных тестов = ~30 секунд.
Стабильность сборок
Было: Каждая третья сборка падала из-за каких-либо проблем.
Стало: Падает только когда разработчик поломал логику какого-то метода.
В дальнейших планах у нас переписывание acceptance (gui) тестов — также запускать их внутри контейнера и изолировать от остального окружения.