Тестирование монорепозитории в Docker контейнере

Kate

Administrator
Команда форума
Хочу поделиться своим опытом реализации тестирования в монорепозитории, без затрагивания написания самих тестов ...

Допустим есть репозиторий с несколькими приложениями и папкой common, в которой находятся функционал, который используется в этих приложениях

.
├── apps
│ ├── app1
│ │ ├── cfg
│ │ │ └── __init__.py
│ │ ├── core
│ │ │ └── __init__.py
│ │ ├── docker-compose.yml
│ │ ├── manage.py
│ │ └── tests
│ │ └── __init__.py
│ ├── app2
│ └── app3
├── common
│ └── __init__.py
├── requirements-dev.txt
└── requirements.txt

При реализации тестов хотелось реализовать следующие задачи:

  • при реализации новых фич в приложениях, запускать тестирование приложений на нескольких версиях python
  • при добавлении нового функционала в common запускать тестирование всех приложений
  • при малейшем изменении в requirements.txt или requirements-dev.txt пересобирать окружение и так же запускать тестирование всех приложений
Эти задачи можно реализовать с помощью tox, добавляем файл ./tox.ini (актуально для tox==3.27.1):

[main]
current_python_env_dir = {toxworkdir}{/}current_python_env_dir
next_python_env_dir = {toxworkdir}{/}next_python_env_dir
report_temp_dir = test{/}temp{/}
pytest_flags = --tb=no

[tox]
skipsdist = True
envlist =
{next_python, current_python}-{app1, app2, app3}

[testenv]
recreate = False
sitepackages = False
passenv =
FORCE_COLOR
commands =
next_python-app1: {env:TOXBUILD:py.test apps{/}app1{/}tests --junitxml={[main]report_temp_dir}test_{envname}.xml {[main]pytest_flags}}
current_python-app1: {env:TOXBUILD:py.test apps{/}app1{/}tests{/}smoke --junitxml={[main]report_temp_dir}test_{envname}.xml {[main]pytest_flags}}

next_python-app2: {env:TOXBUILD:py.test apps{/}app2{/}tests --junitxml={[main]report_temp_dir}test_{envname}.xml {[main]pytest_flags}}
current_python-app2: {env:TOXBUILD:py.test apps{/}app2{/}tests{/}smoke --junitxml={[main]report_temp_dir}test_{envname}.xml {[main]pytest_flags}}

next_python-app3: {env:TOXBUILD:py.test apps{/}app3{/}tests --junitxml={[main]report_temp_dir}test_{envname}.xml {[main]pytest_flags}}
current_python-app3: {env:TOXBUILD:py.test apps{/}app3{/}tests{/}smoke --junitxml={[main]report_temp_dir}test_{envname}.xml {[main]pytest_flags}}

[testenv:next_python-{app1, app2, app3}]
basepython = python3.7
envdir = {[main]next_python_env_dir}
deps =
-Urrequirements-dev.txt

[testenv:current_python-{app1, app2, app3}]
basepython = python3.8
envdir = {[main]next_python_env_dir}
deps =
-Urrequirements-dev.txt
Добавляем докерфайл образа в котором у нас будут проходить тестирование ./test/test_tox.Dockerfile:

FROM python3.7

WORKDIR /tox_workdir
COPY requirements*.txt tox.ini /tox_workdir/

#==================================================================================
# Ставим необходимые зависимости
#==================================================================================
RUN apt-get update && apt-get -y --no-install-recommends install \
cabextract \
curl \
......
&& apt-get clean

#==================================================================================
# Добавляем пользователя webui и далее делаем все через него
#==================================================================================
RUN addgroup --gid 1000 --system web && adduser --system --gid 1000 --uid 1000 web
RUN chown -R web /tox_workdir
USER web

#==================================================================================
# Ставим pyenv, через него устанавливаем необходимые версии python
# и прописываем пути
#==================================================================================
RUN curl https://pyenv.run | bash \
&& echo 'export PYENV_ROOT="/home/web/.pyenv"' >> ~/.bashrc \
&& echo 'export PATH="/home/web/.local/bin:$PATH"' >> ~/.bashrc \
&& echo 'export PATH="$PYENV_ROOT/shims:$PATH"' >> ~/.bashrc \
&& echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc

RUN . ~/.bashrc && pyenv install 3.8
RUN . ~/.bashrc && pyenv global 3.8
RUN . ~/.bashrc && pip3.8 install --upgrade cython
#==================================================================================
# Установка tox и окружений
#==================================================================================

RUN pip3 install tox==3.27.1 && pip3 install --upgrade cython
RUN . ~/.bashrc && bash -c "export TOXBUILD=true && tox"

добавляем скрипт для сборки этого образа ./test/build.sh:

#!/bin/bash
docker build -f test_tox.Dockerfile -t test_tox:latest .
таким образом, после сборки этого образа у нас в контейнере будут установлены 2 версии python и 2 папки с окружениями (с разными версиями python) в которых будут установлены все пакеты из requirements-dev.txt При запуске тестирования из контейнера, окружения пересобираться не будут

Далее для запуска тестов создадим файл ./test/docker-compose.yaml :

version: "2"

services:
db:
image: postgres
environment:
- POSTGRES_USER=root
- POSTGRES_HOST_AUTH_METHOD=trust
volumes:
# инит скрипт для создания баз данных для приложений, можно положить
# рядом, в ./test/create_databases.sql
- ./create_databases.sql:/docker-entrypoint-initdb.d/init.sql
cache:
image: memcached:alpine
test_tox:
image: test_tox
volumes:
- ../:/test
# это нужно для того, чтобы локальные конфиги заменялись конфигами,
# которые подойдут для запуска в контейнере, можно так же положить
# рядом в ./test/container_cfg
- ./container_cfg:/test/apps/app1/cfg
- ./container_cfg:/test/apps/app2/cfg
- ./container_cfg:/test/apps/app3/cfg
volumes:
media:

файл ./test/create_databases.sql :

CREATE DATABASE app1_test;
CREATE DATABASE app2_test;
CREATE DATABASE app3_test;
теперь можно запускать тестирование через docker-compose, для удобства создадим файл ./test/Makefile :

# You can set these variables from the command line.
TOX_WORK_DIR=/tox_workdir/.tox
APP1_ENV=-e current_python-app1,next_python-app1
APP2_ENV=-e current_python-app2,next_python-app2
APP3_ENV=-e current_python-app3,next_python-app3
ALL_ENVS=$(APP1_ENV) $(APP2_ENV) $(APP3_ENV)

define run-test
docker-compose rm --all
docker-compose up -d db
docker-compose up -d cache
docker-compose run --rm test_tox /bin/bash -c ". ~/.bashrc && cd /test/ && tox $1 --workdir $(TOX_WORK_DIR) -q --result-json .tox-result.json" || :
docker-compose down
docker-compose rm --all
endef

build:
./build_local.sh

app1_test:
$(call run-test, $(APP1_ENV))

app2_test:
$(call run-test, $(APP2_ENV))

app3_test:
$(call run-test, $(APP3_ENV))

tests:
$(call run-test, $(ALL_ENVS))

all: build tests

.DEFAULT_GOAL := all

через Makefile мы можем пересобрать тестовый контейнер и запустить тестирование интересующих нас приложений, например:

cd test
make build
make app1_test
make tests
Дерево проекта выглядит следующим образом:

.
├── apps
│ ├── app1
│ │ ├── cfg
│ │ │ └── __init__.py
│ │ ├── core
│ │ │ └── __init__.py
│ │ ├── docker-compose.yml
│ │ ├── manage.py
│ │ └── tests
│ │ └── __init__.py
│ ├── app2
│ └── app3
├── common
│ └── __init__.py
├── requirements-dev.txt
├── requirements.txt
└── test
├── Makefile
├── build.sh
├── container_cfg
│ └── __init__.py
├── create_databases.sql
├── docker-compose.yaml
└── test_tox.Dockerfile
Плюсы такой реализации:

  • Все тестирование выполняется в отдельном контейнере, который уже содержит все необходимое для запуска тестов
  • Можно очень быстро проверить работоспособность приложений, при изменении common, не переходя в папки тестов отдельных подсистем
  • Тестирование приложений с несколькими окружениями (так же можно тестировать с разными версиями определенных пакетов)
  • Наработки из папки ./test/ можно легко прикрутить в CI/CD при запуске тестирования в пайплайне
Минусы:

  • Костыльное вкорячивание 2 версии python в один контейнер
  • Долгая пересборка контейнера за счет установки второй версии python
  • Невозможность запуска тестирования с дополнительными флагами (например --pdb)
P.S. При написании статьи постарался максимально обезличить проект, так что возможны некоторые неточности, главное было донести суть.

 
Сверху