How to Use tox to Test Python Projects
Configure tox 4 with TOML, run tests across Python versions, pass pytest options, and troubleshoot common environment problems.
Use tox to create isolated test environments, install the dependencies each environment needs, and run your test command across the Python versions your project supports. For a new tox 4 setup, put the configuration in tox.toml, define an environment matrix, and run tox from the project root.
The tox documentation describes this workflow as creating virtual environments for multiple Python versions, installing project dependencies, and running tests in each environment (tox command reference). tox runs the commands you configure; it does not determine whether your tests are comprehensive or correct.
1. Install tox and check your project
tox 4 is distributed as a Python package. Install it in a tool environment, such as an activated virtual environment, rather than adding it as a runtime dependency of your application.
python -m pip install tox
Check that tox is available and that the interpreters you want to test with are installed:
tox --version
python --version
python3.12 --version
python3.13 --version
The version commands are examples. Choose a matrix that matches your project’s supported Python versions and the interpreters available on the machine or CI runner. tox can create environments only when it can find a suitable interpreter.
2. Add a tox.toml configuration
Create tox.toml in the project root. This compact configuration runs pytest in Python 3.13 and 3.12 environments:
env_list = ["3.13", "3.12"]
[env_run_base]
deps = ["pytest>=8"]
commands = [["pytest", { replace = "posargs", default = ["tests"], extend = true }]]
The shared env_run_base settings apply to the listed run environments. tox creates each environment, installs pytest there, and runs the command with tests as its default target. The posargs replacement lets you pass extra pytest arguments to that command.
Make sure the project has a tests directory, or change the default to the path your project uses. For example, a project that keeps tests under src/my_package/tests can use that path as the default.
Choose the configuration file format
tox.tomlis the primary file for a new tox configuration.pyproject.tomlis an alternative. Put the same settings under[tool.tox]; for example, use[tool.tox.env_run_base]for the shared environment settings.tox.iniandsetup.cfgare documented legacy formats. They may be present in existing projects, but use TOML for a new configuration.
Use one configuration location for the tox settings so that the source of a resolved value is clear. If a project already has tox configuration, inspect it before adding another file.
3. Run all environments or choose specific ones
From the directory containing tox.toml, run the default environment list:
tox
The first run creates the environments and installs their dependencies. tox stores these environments in .tox beside the configuration by default. Add .tox/ to the repository’s ignore rules if it is not already ignored.
To run one environment, select it by name:
tox run -e 3.13
To run a comma-separated selection:
tox run -e 3.13,3.12
List the environments tox has configured with:
tox list
When a selected environment name is not configured, tox may still run it with defaults. If a typo appears to succeed, check tox list and the resolved configuration rather than assuming the intended environment ran.
4. Pass options to pytest
Put -- after tox’s own options to pass arguments to the configured command. For example, add verbose pytest output:
tox run -e 3.13 -- -v
Run a particular test file with a keyword filter:
tox run -e 3.13 -- tests/test_api.py -k authentication
Because the configuration uses replace = "posargs" with extend = true, tox appends these arguments to the command and retains the default tests target. If you prefer to pass a complete replacement argument list, remove extend = true and supply the target explicitly when invoking tox. The default behavior is useful for adding flags such as -v; a replacement is useful when the invocation should choose a different test target.
5. Run environments in parallel safely
Sequential runs are easier to inspect. Parallel execution can shorten a matrix run when environments are independent, but tests that write to a shared temporary directory can collide.
Give pytest a distinct base temporary directory inside each tox environment by extending the command:
env_list = ["3.13", "3.12"]
[env_run_base]
deps = ["pytest>=8"]
commands = [["pytest", "--basetemp={env_tmp_dir}", { replace = "posargs", default = ["tests"], extend = true }]]
Then run the selected environments in parallel:
tox parallel -e 3.13,3.12
Isolation matters when tests use temporary files, plugins, or fixtures that otherwise select the same path. If the suite or its external services are not safe to run concurrently, use the normal sequential tox run command.
6. Reuse, refresh, or skip environment installation
After the first run, tox reuses prepared environments unless dependency changes require an update. Reuse avoids rebuilding everything on every invocation, while keeping test commands inside their configured environments.
Force tox to recreate an environment when it appears stale or its interpreter or dependencies have changed:
tox run -e 3.13 -r
Skip installation for an already prepared environment when you deliberately want to reuse its current packages, including when rerunning offline:
tox run -e 3.13 --skip-env-install
Skipping installation does not repair a missing or outdated dependency. Use a normal run when you need tox to install or update the configured dependencies.
7. Configure tests for a real project
The minimal example installs pytest but assumes the application code is importable in the environment. For an installable project, add the project itself to the environment dependencies. A common arrangement is to install the package in editable mode along with pytest:
env_list = ["3.13", "3.12"]
[env_run_base]
package = "editable"
deps = ["pytest>=8"]
commands = [["pytest", "--basetemp={env_tmp_dir}", { replace = "posargs", default = ["tests"], extend = true }]]
Use the packaging behavior appropriate for the project. If it is not an installable package, configure its import path or working directory deliberately rather than assuming tox will make source files importable.
Add separate environments for other checks, such as linting, by listing them in env_list and defining their commands. For example, a lint environment can install the chosen linter and invoke it on the project’s source and tests. Keep each environment’s dependencies scoped to the work it performs so test-only tools do not become application dependencies.
8. Troubleshoot failed or surprising runs
| Symptom | Likely cause | What to do |
|---|---|---|
| tox cannot find the requested Python interpreter | The interpreter is not installed or is not discoverable under the expected name. | Install the requested Python version or change env_list to an available supported version; inspect tox’s verbose output. |
| Imports fail although tests pass in the developer environment | The project package is not installed into the tox environment, or the source layout is not on its import path. | Configure project packaging, install the project in the test environment, or explicitly set the required working directory or import path. |
| Pytest reports that no tests were found | The default path tests does not match the repository’s test layout, or a passed positional argument replaced the default. |
Set the correct default path in commands and pass the intended path after --. |
| A selected environment unexpectedly succeeds | tox can use defaults for an unconfigured environment name, which can hide a typo. | Run tox list and inspect the selected environment’s resolved settings. |
| Parallel tests overwrite files or fail intermittently | Environments share a pytest temporary path or another test resource. | Use --basetemp={env_tmp_dir} and isolate any other shared files, ports, or external resources. |
| Changes to dependencies do not appear | An existing environment may be stale, or installation was skipped. | Run normally to install dependencies, or recreate the environment with -r. |
| A command fails but its settings are unclear | The effective configuration differs from what was expected. | Use tox config to inspect resolved values before changing the configuration. |
Start diagnosis with the resolved settings for a specific environment:
tox config -e 3.13 -k deps commands
Increase verbosity and inspect tox’s environment logs:
tox run -e 3.13 -vv
ls .tox/3.13/log/
The environment name in the log path follows tox’s actual name for that environment. To inspect the prepared environment directly, run a command inside it:
tox exec -e 3.13 -- python --version
tox exec -e 3.13 -- pip list
If the environment is stale, recreate it and rerun the failing command. If the failure persists in a fresh environment, examine the test failure itself, package installation, and any external services the tests depend on.
9. Keep runs predictable in local work and CI
- Keep
env_listaligned with the Python versions the project actually supports. - Ensure CI runners have every selected interpreter available.
- Use the same tox commands locally and in CI so environment setup and test invocation stay consistent.
- Use parallel mode only after separating temporary paths and other shared test resources.
- Use normal installation when dependency state must be current; use
--skip-env-installonly for deliberate reuse. - Ignore the local
.toxdirectory rather than committing generated environments.
10. Or skip the browser setup
tox is for Python test environments; if the testing work also needs website screenshots, ScreenshotNeo provides a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Learn about ScreenshotNeo, or sign up free for 1,000 screenshots a month with no card.
11. Frequently asked questions
Does tox run pytest automatically?
No. tox runs the commands configured for each environment. Add pytest as a dependency and configure a pytest command, as in the example above.
Should tox be a project dependency?
tox is a development tool that orchestrates environments and commands. Install it in the project’s tooling setup or CI environment rather than making it a runtime dependency of the application.
Can I keep an existing tox.ini?
Existing projects can maintain their current configuration while migrating deliberately. For a new setup, the tox documentation recommends TOML configuration.
Does a successful tox run prove the package supports every listed Python version?
It shows that the configured commands completed in the environments that ran. The result depends on the tests and checks configured for those environments; tox does not guarantee coverage or correctness.


