И вот уже прошло почти полгода, как мы переписали свои старые необтёсанные, долгие и не стабильные функциональные тесты и перешли на быстрые, ни от чего не зависящие компонентные. Поэтому, пора делиться :)
Для тех кто не знает, компонентные тесты — это тесты которые полностью изолированы от глобального окружения и позволяют проверить те или иные кейсы, которые 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_ADDRlocal-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) тестов — также запускать их внутри контейнера и изолировать от остального окружения.
Комментарии (11)
Aquahawk
15.11.2018 08:08Постепенно двигаюсь к пониманию того что наборы тестов должны быть поделены на несколько suite. Долгие, быстрые, минимальный регресс, подзадачные. Долгие собираются и гоняются раз в сутки или перед выкаткой например, могу длиться и час. Быстрые и минимал регресс гонять после каждого коммита, а подзадачные это мини сьют у каждого разработчика свой, где он включает только нужные тесты и при разработке гоняет только их, и только перед коммитом прогоняет локально быстрые и минимальный регресс. Пока у себя не внедрил, сейчас у нас есть по сути подзадачный, быстрый и медленный наборы.
Tumenbayev Автор
15.11.2018 08:16Мы используем codeception и у каждого разработчика есть свой конфиг файл с настройками его окружения, а также есть возможность запускать тесты по файлово и группам.
И еще сейчас, у нас есть 4 suite (Acceptance, Component, Unit, Newman, Smoke).
Немного о newman: habr.com/company/kolesa/blog/353902
jetcar
15.11.2018 11:37также нередко приходилось их перезапускать, чтобы добиться зеленой сборки,
вот в этом месте надо было фиксить проблему, а не мокая апи, если чтото замокано то функциональным тестом это больше назвать низя, и нужно писать ещё тесты которые без мока будут работать, а вашу проблему можно решить через докеры или деплой переделать так чтоб сервис был 99.9% времени в рабочем состоянии, с продакшеном же та же проблема когда релиз происходитTumenbayev Автор
15.11.2018 11:50Тут дело такое…
Где-то могут вестись какие-либо работы, может что-то произойти с базой.
Но, замечания верные. Спасибо.jetcar
15.11.2018 12:00с базой это наверно самое сложное, как делать миграции и ничего не ломать, но как уже писал это нужно и для продакшена, хорошо если можно на выходных или в нерабочие часы задеплоить и не сломать ничего, но с ростом проекта это окно всё меньше и меньше и чем раньше начать деплой правильно делать тем лучше, говорят есть приложения которые годами работают без перебоев :)
Tumenbayev Автор
15.11.2018 13:25У нас большое количество разработчиков, у каждого из них подняты локально тестовые окружения и все они собирают сборки в bamboo, что не может не сказаться на нагрузке на тестовый АПИ и другие сервисы, поэтому это всё дорого.
jehy
15.11.2018 12:48Мы проблему решаем похожим способом — вот только у нас слишком много всего может меняться в ответах, поэтому мы написали свой сервис, в котором вместо статики мы используем режим обучения — сначала мок сервер является прокси к настоящим сервисам, а потом на основании накопленных данных превращается в быстрый мок сервер.
AigizK
Чтоб ускорить скорость прохождения тестов в десятки раз, достаточно в самом начале разработки поставить Sleep(N minutes), а потом убрать.
А если серьезно, мокая что то, скорее всего надо будет написать интеграционный тест.
В предыдущем проекте было более 5000 сценариев, скорость выполнения 100-500 сценариев/сек в зависимости от машины. Но там отдельно тестировали сценариями бекенд, а для фронтэнда использовали силениум.
В новом проекте с помощью этого github.com/GoogleChrome/puppeteer пытаемся объединить сценарии БЭ+ФЭ
Tumenbayev Автор
Мы сейчас на основе этого же мок-сервера изолировано пытаемся прогонять frontend средствами selenium. А в сторону puppeteer не смотрел, спасибо.