Запуск тестов в контейнерах с помощью docker-compose
Основными преимуществами этого способа являются наличие независимой среды для запуска тестов и снижение сложности настройки тестовой среды. Просто загрузите и запустите тесты. Подумайте, как этого добиться.
Ниже вы увидите, как настроить docker-compose для обычного приложения Ruby on Rails. В качестве бонуса вы сможете повторно использовать эти настройки во всех проектах без особых изменений.
Чего мы хотим достичь
Проведение тестов должно быть простым. Новые разработчики должны иметь возможность присоединиться к процессу разработки без особых проблем с настройкой тестовой среды. И это может стать относительно сложным, включая набор интеграции с кучей дополнительных сервисов. Например, для этого требуется работающая база данных.
Тестовые прогоны должны быть изолированными и повторяемыми. Вы не хотите, чтобы один ненадежный тест провалился только на вашей машине. Неудачный тест должен провалиться, если его запускает кто-либо из команды, а также в CI.
Тестовая среда должна быть максимально приближена к производственной среде. Проведение интеграционных тестов «зеленой системы» должно гарантировать, что функция работает в рабочей среде. По мере роста вашего приложения количество зависимостей служб также может расти, поэтому важно убедиться, что интеграция с ними работает правильно, и поддерживать зависимости в актуальном состоянии.
Войдите в Docker
Использование Docker для запуска тестов может помочь вам решить эти проблемы. У всех разработчиков будет одинаковая изолированная тестовая среда, которую также можно использовать для запуска тестов в CI. Новым разработчикам не придется тратить полдня только на то, чтобы настроить все необходимое для запуска тестов.
Тесты будут запускаться внутри контейнера, поэтому вам необходимо его определить. Это делается в Dockerfile:
FROM cimg/ruby:2.7.1
ARG TINI_VERSION=v0.19.0
RUN sudo apt-get update -qq \
&& sudo apt-get install -yq --no-install-recommends \
libxml2-dev libxslt-dev libtool pkg-config \
libbz2-dev libglib2.0-dev libxml2-dev libxslt-dev cmake \
&& sudo apt-get clean \
&& sudo rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& sudo truncate -s 0 /var/log/*log
ENV BUNDLE_JOBS=4 BUNDLE_RETRY=3
RUN gem update --system && gem install rake bundler --no-document
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/local/bin/tini
RUN sudo chmod a+x /usr/local/bin/tini
ENTRYPOINT ["/usr/local/bin/tini", "--"]
Сначала определите базовый образ, здесь мы используем официальный образ CircleCI. Затем установите необходимые зависимости приложения и настройте владение и права на файлы с смонтированных томов (подробнее об этом позже). В конце определите точку входа в Tini.
Tini — это удобный инструмент, который помогает собирать любые процессы-зомби и пересылать сигналы командам, выполняемым в контейнере.
Подключение
Для выполнения задач приложения могут потребоваться дополнительные зависимости от служб. В этом примере для запуска тестов приложению необходим доступный экземпляр базы данных. Это можно сделать с помощью файла docker-compose
.
version: "3.8"
services:
app:
build:
context: .
dockerfile: ./Dockerfile
environment:
DATABASE_URL: postgresql://root@db:5432/db_name
DISABLE_SPRING: 1
MALLOC_ARENA_MAX: 2
PARALLEL_WORKERS: 1
PGHOST: db
PGUSER: root
RAILS_ENV: ${RAILS_ENV:-test}
networks:
default:
user: ${CURRENT_UID:?"Please run as follows 'CURRENT_UID=$(id -u):$(id -g) docker-compose up'"}
volumes:
- .:/home/circleci/project:cached
- gems:/home/circleci/.rubygems
- cache:/home/circleci/.cache
depends_on:
- db
db:
image: circleci/postgres:alpine
restart: always
environment:
POSTGRES_USER: root
POSTGRES_DB: db_name
volumes:
- pg:/var/lib/postgresql/data
volumes:
gems:
cache:
pg:
Здесь мы определяем две службы: app
и db
. Служба приложений создается с использованием определенного Dockerfile, здесь также задаются все необходимые переменные среды.
В разделе томов сначала монтируется каталог приложения, остальные тома добавляются в целях кэширования. Здесь также указано, что служба приложений зависит от службы БД.
Обратите внимание, что мы указываем пользователя для службы приложений, и этот пользователь должен быть передан вызывающей стороной команды docker-compose
.
Это сделано для решения проблемы с разрешениями, которая может возникнуть, когда процессам необходимо изменить файлы внутри смонтированных томов или когда вам потребуется доступ к файлам (журналам, снимкам экрана и другим артефактам), сгенерированным внутри контейнера. Чтобы решить обе эти проблемы, вам необходимо передать текущий идентификатор пользователя командам docker-compose
.
Конфигурация службы базы данных состоит из имени образа (предоставленного CircleCI
), учетных данных доступа к БД в переменных среды и тома для хранения данных БД.
Применение: Docker и docker-compose сейчас проделывают большую работу, чтобы облегчить жизнь разработчикам, но нужно еще многое запомнить и набрать:
export CURRENT_UID=$(id -u):$(id -g)
docker-compose up --remove-orphans -d db
docker-compose build app
docker-compose run app bin/bundle install
docker-compose run app bin/rails db:prepare
docker-compose run app bin/rails bin/rails db:schema:load
docker-compose run app bin/rspec
Все это можно извлечь в несколько служебных скриптов:
#!/usr/bin/env bash
echo "=> Install dependencies"
bin/bundle install
echo "=> Prepare DB"
bin/rails db:prepare
bin/rails db:schema:load
#!/usr/bin/env bash
echo "=> Build"
export CURRENT_UID=$(id -u):$(id -g)
docker-compose up --remove-orphans -d db
docker-compose build app
echo "=> Setup"
docker-compose run app bin/ci-setup
#!/usr/bin/env bash
echo "=> Run tests"
export CURRENT_UID=$(id -u):$(id -g)
docker-compose run app bin/rspec
Теперь настройка и запуск тестов внутри docker-контейнера достигается только запуском:
bin/dc-setup
bin/dc-test
Docker — мощный инструмент, который можно использовать в процессе разработки. Это может сделать запуск и переключение между проектами быстрым и легким, а также помочь гарантировать, что все будут в курсе используемых технологий.