Хочу поделиться своим опытом реализации тестирования в монорепозитории, без затрагивания написания самих тестов ...
Допустим есть репозиторий с несколькими приложениями и папкой 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. При написании статьи постарался максимально обезличить проект, так что возможны некоторые неточности, главное было донести суть.
Alexufo
docker-compose поддерживает inline создания контейнеров dockerfile_inline, удобно, когда конфиг небольшой, но я не разобрался, как заставить каждый раз при старте docker-compose делать билд образа, он кешируется, зараза. Приходится удалять образ до запуска.
pqbd