Aller au contenu
Développement medium

Tox : tester son code Python sur plusieurs versions

14 min de lecture

logo tox

tox automatise le test d'un projet Python dans plusieurs environnements isolés, en particulier sur plusieurs versions de Python. À partir d'un seul fichier tox.ini, il crée les environnements virtuels, installe le paquet et ses dépendances, puis lance les tests sur chaque combinaison. Sans tox, il faudrait entretenir ces environnements à la main, une tâche fastidieuse et difficile à reproduire en équipe. Ce guide couvre l'installation, le fichier tox.ini, le test multi-versions, l'exécution parallèle et l'intégration d'outils de qualité.

Il s'adresse aux personnes à l'aise avec pytest qui veulent garantir que leur code fonctionne sur toutes les versions qu'elles supportent, et préparer le terrain pour la CI/CD. Les exemples ont été validés avec tox 4.

  • Installer tox proprement et générer un premier tox.ini.
  • Comprendre la structure du fichier tox.ini (environnements, dépendances, commandes).
  • Tester un projet sur plusieurs versions de Python.
  • Lancer les environnements en parallèle et cibler un environnement précis.
  • Intégrer des outils de qualité (linter, typage) dans tox.

Tox est conçu pour simplifier la vie des développeurs et des administrateurs système en automatisant plusieurs tâches récurrentes dans les projets Python. Son principal objectif : vous permettre de tester votre code dans différents environnements Python sans avoir à les gérer manuellement.

  • Gestion multi-environnements : L'une des fonctionnalités phares de Tox est la gestion multiples versions de Python. Grâce à un simple fichier de configuration, vous pouvez exécuter vos tests sur Python 3.8, 3.9, 3.10 et plus encore. Cela garantit que votre code reste fonctionnel sur toutes les versions que vous souhaitez supporter. Pour installer plusieurs versions de Python, vous pouvez utiliser pyenv ou asdf.

  • Isolation des environnements : Chaque environnement défini dans Tox est isolé, comme si vous utilisiez un virtualenv. Cela signifie que les dépendances installées pour un test ne polluent pas votre environnement global. Vous évitez ainsi les conflits de versions et les surprises en production.

  • Automatisation des tâches courantes : Tox ne se limite pas aux tests unitaires.

    Il peut aussi :

    • Lancer des outils de qualité de code comme flake8, black ou pylint
    • Exécuter des vérifications de type avec mypy;
    • Générer de la documentation avec sphinx;
    • Publier un paquet sur PyPI;
    • Nettoyer les fichiers temporaires ou les caches;
  • Extensibilité avec des plugins : Tox dispose aussi d’un système de plugins qui permet d’ajouter des fonctionnalités spécifiques. Par exemple, vous pouvez utiliser le plugin tox-docker pour ajouter des services à vos tests.

  • Exécution en parallèle : Pour gagner du temps, Tox permet d'exécuter plusieurs environnements en parallèle avec l’option --parallel.Cela est particulièrement utile dans les projets complexes où les tests prennent plusieurs minutes.

Toutes ces fonctionnalités font de Tox un outil polyvalent, capable de centraliser et d’automatiser la majorité des vérifications que vous devez faire avant de livrer votre code.

Pour installer Tox, il vous suffit d'utiliser pipx ou pip :

Fenêtre de terminal
pipx install tox

ou

Fenêtre de terminal
pip install tox

Pour illustrer l'utilisation de Tox, nous allons créer une simple application de ligne de commande (CLI) en Python. Cette application affichera un message de salutation personnalisé. Nous utiliserons le module Click pour créer la CLI et Tox pour tester notre application sur plusieurs versions de Python.

Voici la structure de répertoires de notre projet :

Fenêtre de terminal
tree
mon_cli/
├── src/
└── mon_cli/
├── __init__.py
└── cli.py
├── tests/
└── test_cli.py
├── setup.py
├── tox.ini
├── requirements.txt
  • mon_cli/ : Répertoire principal de notre projet.
  • src/ : Répertoire source contenant le code de notre application.
  • mon_cli/ : Répertoire contenant le code de notre application :
    • __init__.py : Fichier d'initialisation du module.
    • cli.py : Fichier contenant le code de notre application CLI.
  • setup.py : Fichier de configuration pour l'installation de notre projet.
  • tox.ini : Fichier de configuration pour Tox.
  • requirements.txt : Fichier contenant les dépendances de notre projet.
  • tests/ : Répertoire contenant les tests de notre application:
    • test_cli.py : Fichier contenant les tests de notre application.

Pour créer cette structure de projet, vous pouvez exécuter les commandes suivantes :

Fenêtre de terminal
# 1. Créer le dossier racine du projet
mkdir moncli && cd moncli
# 2. Créer l'arborescence du code source
mkdir -p src/mon_cli
# 3. Créer les fichiers Python de base
touch src/mon_cli/__init__.py
cat > src/mon_cli/cli.py << 'EOF'
import click
@click.command()
@click.option("--name", default="Monde", help="Nom à saluer.")
def hello(name):
"""Affiche un message de salutation."""
click.echo(f"Bonjour, {name} !")
if __name__ == "__main__":
hello()
EOF
# 4. Créer le fichier de test
mkdir -p tests
cat > tests/test_cli.py << 'EOF'
from click.testing import CliRunner
from mon_cli.cli import hello
def test_hello_default():
runner = CliRunner()
result = runner.invoke(hello)
assert result.exit_code == 0
assert "Bonjour, Monde !" in result.output
def test_hello_custom_name():
runner = CliRunner()
result = runner.invoke(hello, ["--name", "Alice"])
assert result.exit_code == 0
assert "Bonjour, Alice !" in result.output
EOF
# 5. Créer le fichier setup.py
cat > setup.py << 'EOF'
from setuptools import setup, find_packages
setup(
name="moncli",
version="0.1.0",
packages=find_packages(where="src"),
package_dir={"": "src"},
install_requires=["click"],
entry_points={
"console_scripts": [
"moncli = mon_cli.cli:hello",
],
},
)
EOF
# 6. Créer requirements.txt
echo "click>=8.1" > requirements.txt

Étape 2 : Création du fichier de configuration tox.ini

Section intitulée « Étape 2 : Création du fichier de configuration tox.ini »

Créez un fichier tox.ini à la racine de notre projet, nous allons utiliser la commande suivante :

Fenêtre de terminal
tox quickstart

Ce qui va créer un fichier tox.ini avec le contenu suivant :

[tox]
env_list =
py312
minversion = 4.25.0
[testenv]
description = run the tests with pytest
package = wheel
wheel_build_env = .pkg
deps =
pytest>=6
commands =
pytest {tty:--color=yes} {posargs}

Lancez :

Fenêtre de terminal
tox
py312: install_deps> python -I -m pip install 'pytest>=6'
.pkg: install_requires> python -I -m pip install 'setuptools>=40.8.0' wheel
.pkg: _optional_hooks> python /home/bob/.local/share/pipx/venvs/tox/lib/python3.12/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_wheel> python /home/bob/.local/share/pipx/venvs/tox/lib/python3.12/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: build_wheel> python /home/bob/.local/share/pipx/venvs/tox/lib/python3.12/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
py312: install_package_deps> python -I -m pip install click
py312: install_package> python -I -m pip install --force-reinstall --no-deps /home/bob/Projets/moncli/.tox/.tmp/package/1/moncli-0.1.0-py3-none-any.whl
py312: commands[0]> pytest --color=yes
====================================================== test session starts =======================================================
platform linux -- Python 3.12.3, pytest-8.3.5, pluggy-1.5.0
cachedir: .tox/py312/.pytest_cache
rootdir: /home/bob/Projets/moncli
collected 2 items
tests/test_cli.py .. [100%]
======================================================= 2 passed in 0.01s ========================================================
py312: OK (3.13=setup[3.03]+cmd[0.10] seconds)
congratulations :) (3.16 seconds)

Pour tester votre projet avec Python 3.11, il faut :

  1. Installer Python 3.11 localement (via pyenv recommandé)
  2. S’assurer que Tox peut le détecter
  3. Ajouter py311 à la configuration tox.ini

Dans un premier temps, nous allons installer plusieurs versions de Python avec pyenv :

Fenêtre de terminal
pyenv install 3.10.13
pyenv install 3.11.9
pyenv install 3.12.2
Fenêtre de terminal
pyenv global 3.10.13 3.11.9 3.12.2
pyenv rehash

Vérifiez :

Fenêtre de terminal
which python3.11
/home/bob/.pyenv/shims/python3.11

Modifier le fichier tox.ini

Ajoutez py310 et py311 à la liste des environnements :

env_list =
py310
py311
py312
minversion = 4.25.0
[testenv]
description = run the tests with pytest
package = wheel
wheel_build_env = .pkg
deps =
pytest>=6
commands =
pytest {tty:--color=yes} {posargs}

4. Lancer les tests sur toutes les versions disponibles

Section intitulée « 4. Lancer les tests sur toutes les versions disponibles »
Fenêtre de terminal
tox

Ou pour les exécuter plus rapidement en parallèle :

Fenêtre de terminal
tox --parallel

Nous avons maintenant un projet Python qui utilise Tox pour tester notre application CLI sur plusieurs versions de Python. Vous pouvez facilement ajouter d'autres environnements ou dépendances en modifiant le fichier tox.ini. Cela vous permet de vous concentrer sur le développement de votre application sans vous soucier de la compatibilité entre les différentes versions de Python.

La commande tox propose de nombreuses options pour gérer vos environnements, exécuter des tests ciblés ou analyser votre configuration. Voici les plus utiles :

Fenêtre de terminal
tox -av
default environments:
py310 -> run the tests with pytest
py311 -> run the tests with pytest
py312 -> run the tests with pytest

Affiche la liste des environnements disponibles, avec leurs descriptions si elles sont définies dans tox.ini.

Fenêtre de terminal
tox -e py311

Permet de tester uniquement Python 3.11.

Fenêtre de terminal
tox -- tests/test_cli.py::test_hello_default

Exécute uniquement un test ciblé, ici test_hello_default.

Fenêtre de terminal
tox --recreate

Utile après une modification de dépendance ou de Python.

Fenêtre de terminal
tox --parallel auto
py312: OK in 0.78 seconds
py311: OK in 0.8 seconds
py310: OK (0.81=setup[0.69]+cmd[0.12] seconds)
py311: OK (0.80=setup[0.68]+cmd[0.12] seconds)
py312: OK (0.78=setup[0.66]+cmd[0.11] seconds)
congratulations :) (0.84 seconds)

Lance les tests en parallèle, pour accélérer l’exécution sur plusieurs cœurs.

Tox permet bien plus que de simplement exécuter des tests unitaires. Voici des cas d’usage plus poussés :

Vous pouvez configurer des environnements pour :

  • Linter le code avec flake8
  • Vérifier les types avec mypy
  • Formater avec black
[testenv:lint]
deps = flake8
commands = flake8 src tests
[testenv:typecheck]
deps = mypy
commands = mypy src

Pour tout exécuter :

Fenêtre de terminal
tox -e lint,typecheck,py311,py312

Pour tirer le meilleur parti de Tox, voici quelques conseils pratiques :

  • Utilisez des noms clairs pour vos environnements (ex. test, lint, doc).
  • Séparez les dépendances dans des fichiers dédiés (requirements-test.txt, requirements-dev.txt).
  • Centralisez la logique dans des scripts Python ou Makefile pour faciliter l’exécution manuelle.
  • Validez la compatibilité des versions de Python avec vos dépendances (via python_requires dans setup.py).
  • Ajoutez Tox à votre pipeline CI/CD (Gitlab CI, GitHub Actions…) pour automatiser les tests dès chaque commit : la CI lance la même commande tox que vous en local, sur toutes les versions.
  • tox teste un projet dans plusieurs environnements isolés à partir d'un seul tox.ini.
  • La section [tox] liste les environnements (env_list), la section [testenv] décrit dépendances et commandes.
  • Il crée les environnements virtuels, installe le paquet et lance pytest dans chacun.
  • tox -e py311 cible un environnement, tox --parallel auto accélère l'exécution.
  • tox ne se limite pas aux tests : il orchestre aussi linter, typage et autres tâches de qualité.
  • La même commande tox tourne en local et en CI/CD, ce qui garantit la reproductibilité.

tox orchestre des tests écrits avec un framework et sur plusieurs versions installées localement. Voici les briques qui s'y rattachent.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn