tox is a generic virtual environment management and test automation tool for Python projects. Its primary purpose is to standardize and automate the process of testing Python projects against different Python versions and dependency configurations, ensuring that tests are run in isolated, consistent environments.
Why use tox?
1. Isolation: tox creates temporary virtual environments for each test run. This prevents conflicts between your project's dependencies and your system-wide Python installations, as well as between different sets of project dependencies (e.g., testing with different versions of a library).
2. Consistency: It ensures that your tests (and other tasks like linting, documentation building, or packaging) are executed in a consistent manner, regardless of the developer's local setup or the CI environment.
3. Multi-Python Version Testing: Easily test your project against multiple Python interpreters (e.g., Python 3.8, 3.9, 3.10, PyPy) to verify compatibility.
4. Automation of Development Tasks: Beyond testing, tox can automate linting, code style checks, documentation generation, building distributions (wheels, sdist), and more, by defining separate 'testenvs' for each task.
5. CI/CD Integration: tox configurations are easily integrated into Continuous Integration (CI) and Continuous Deployment (CD) pipelines, providing a reliable and reproducible way to validate changes.
6. Dependency Management: It handles the installation of project-specific dependencies (including the project itself) and test-specific dependencies into each isolated environment.
How tox works:
1. Configuration (`tox.ini`): You define your project's testing and development environments in a `tox.ini` file located at the root of your project.
2. Environment Creation: When `tox` is run, it reads `tox.ini` and for each defined 'testenv' (e.g., `py38`, `lint`):
- It creates a dedicated virtual environment (usually in a `.tox` directory at the project root).
- It installs the specified Python interpreter (e.g., `python3.8`).
3. Dependency Installation: It installs the defined dependencies (`deps`) into the newly created virtual environment. This often includes the project itself (e.g., `pip install .`).
4. Command Execution: It executes the specified commands (`commands`) within that virtual environment (e.g., `pytest`).
5. Cleanup (Optional): After execution, the virtual environments are retained for faster subsequent runs, but can be cleaned up using `tox -r`.
Key Concepts:
- `tox.ini`: The central configuration file.
- `[tox]` section: Global settings for tox, like `min_version` and `env_list` (the default environments to run).
- `[testenv]` section: Defines a base configuration that all specific test environments inherit from. This is useful for common dependencies or commands.
- `[testenv:name]` sections: Define specific test environments (e.g., `[testenv:py38]`, `[testenv:lint]`). These can override or extend the base `[testenv]` settings.
- `deps`: A list of dependencies to install into the environment. These are usually test runners (like pytest), linters (like flake8), or specific versions of libraries you want to test against.
- `commands`: The list of shell commands to execute within the virtual environment. This is where you run your tests, linters, or other tools.
- `base_python`: Specifies which Python interpreter to use for a particular test environment.
- `skip_install`: A boolean flag to control whether tox should install your project package itself into the virtual environment (useful for linting environments that don't need the package installed).
Example Code
Project Structure:
my_project/
├── my_package/
│ └── my_module.py
├── tests/
│ └── test_example.py
├── setup.py
└── tox.ini
1. File: my_package/my_module.py
(A simple module for our project)
-----------------------------------
def add(a, b):
return a + b
def subtract(a, b):
return a - b
2. File: tests/test_example.py
(Our unit tests using pytest)
-----------------------------------
from my_package.my_module import add, subtract
def test_add():
assert add(1, 2) == 3
assert add(0, 0) == 0
assert add(-1, 1) == 0
def test_subtract():
assert subtract(5, 2) == 3
assert subtract(2, 5) == -3
assert subtract(0, 0) == 0
3. File: setup.py
(Standard Python package setup file for tox to install our package)
-----------------------------------
from setuptools import setup, find_packages
setup(
name='my_package',
version='0.1.0',
packages=find_packages(),
install_requires=[], No external runtime dependencies for this simple example
python_requires='>=3.8'
)
4. File: tox.ini
(The tox configuration file)
-----------------------------------
[tox]
Minimum version of tox required
min_version = 4.0
The list of environments to run by default when you just type 'tox'
env_list = py38, py39, lint
[testenv]
This tells tox to install your project package into the virtual environment.
It will look for setup.py or pyproject.toml and run 'pip install -e .'
skip_install = False
Dependencies that are common to all test environments (e.g., the test runner)
deps =
pytest
Commands to run in each test environment
commands =
pytest {posargs}
[testenv:py38]
Specific Python interpreter to use for this environment
Ensure Python 3.8 is installed on your system or specified in your CI config
base_python = python3.8
[testenv:py39]
Specific Python interpreter to use for this environment
Ensure Python 3.9 is installed on your system or specified in your CI config
base_python = python3.9
[testenv:lint]
For linting, we typically don't need to install the package itself,
just the linter and potentially its plugins.
skip_install = True
Dependencies for the linting environment
deps =
flake8
Add any flake8 plugins here if needed, e.g., flake8-bugbear
Commands to run for linting
This command checks 'my_package' directory and 'tests' directory for linting issues
commands =
flake8 my_package tests
How to run this example:
1. Create the files and directories as shown above.
2. Open your terminal in the 'my_project' directory.
3. Make sure 'tox' is installed: pip install tox
4. Run tox: tox
tox will create two Python environments (py38, py39) and a lint environment.
It will install 'pytest' and your 'my_package' in py38 and py39, then run tests.
It will install 'flake8' in 'lint' and run flake8.
5. You can also run specific environments, e.g., 'tox -e py38' or 'tox -e lint'.








tox