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