Aller au contenu

Tox : Tests Python multi-environnements

Mise à jour :

logo tox

Tox répond à un problème récurrent dans les projets Python : tester son code de manière cohérente sur plusieurs versions de Python et avec des dépendances variées, sans devoir tout gérer manuellement. Sans Tox, il faudrait créer et entretenir plusieurs environnements virtuels, installer les bonnes versions des paquets, puis lancer les tests à la main pour chaque combinaison. C’est fastidieux, source d’erreurs et difficile à reproduire dans une équipe ou une pipeline CI/CD. Tox automatise tout ce processus et garantit que votre code reste fiable, quel que soit le contexte d’exécution.

Fonctionnalités de 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.

Installation de Tox

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

Terminal window
pipx install tox

ou

Terminal window
pip install tox

Tutoriel de l’utilisation de 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.

Étape 1 : Créer un projet CLI avec Click

Voici la structure de répertoires de notre projet :

Terminal window
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 :

Terminal window
# 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

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

Terminal window
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}

Étape 3 : Exécuter Tox

Lancez :

Terminal window
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)

Étape 4 : Ajouter Python 3.11 pour les tests Tox

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 :

Terminal window
pyenv install 3.10.13
pyenv install 3.11.9
pyenv install 3.12.2
Terminal window
pyenv global 3.10.13 3.11.9 3.12.2
pyenv rehash

Vérifiez :

Terminal window
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

Terminal window
tox

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

Terminal window
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.

Commandes utiles de la CLI Tox

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 :

Lister tous les environnements

Terminal window
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.

Lancer un environnement spécifique

Terminal window
tox -e py311

Permet de tester uniquement Python 3.11.

Passer des arguments à la commande de test

Terminal window
tox -- tests/test_cli.py::test_hello_default

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

Forcer la recréation des environnements

Terminal window
tox --recreate

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

Exécuter en parallèle

Terminal window
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.

Utilisations avancées de Tox

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

Intégration d’outils de qualité de code

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 :

Terminal window
tox -e lint,typecheck,py311,py312

Bonnes pratiques avec Tox

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. Nous verrons cela dans un prochain guide.

Conclusion

Tox est un outil puissant pour automatiser les tests et la qualité de code dans les projets Python. Grâce à sa simplicité d’utilisation et à sa flexibilité, vous pouvez facilement l’intégrer dans votre flux de travail. Que vous soyez un développeur solo ou que vous travailliez dans une grande équipe, Tox vous permet de garantir que votre code fonctionne sur toutes les versions de Python et avec toutes les dépendances nécessaires. En utilisant Tox, vous réduisez le risque d’erreurs et améliorez la qualité de votre code, tout en vous concentrant sur ce qui compte vraiment : le développement de fonctionnalités et la satisfaction de vos utilisateurs.

Pour vous montrer un cas d’utilisation concret, j’ai écrit un guide sur l’utilisation de Tox avec Molecule pour tester vos rôles Ansible. Vous pouvez le trouver ici.

Ressources supplémentaires