Nox Cheat Sheet
Overview
Nox is a Python test automation tool similar to tox but uses Python scripts (noxfile.py) instead of configuration files. This approach provides full Python expressiveness for defining test sessions, including conditionals, loops, and function composition. Nox is used by many Google open-source Python projects.
Nox creates isolated virtual environments for each session, installs dependencies, and runs commands. Its Python-based configuration makes it easier to handle complex testing scenarios, dynamic dependency resolution, and conditional session logic compared to INI-based alternatives.
Installation
pip install nox
# or
pipx install nox
# Verify
nox --version
Core Commands
| Command | Description |
|---|---|
nox | Run all default sessions |
nox -s tests | Run specific session |
nox -s tests-3.12 | Run parametrized session |
nox -l | List available sessions |
nox -r | Reuse existing virtualenvs |
nox --no-venv | Skip virtualenv creation |
nox -k "not lint" | Filter sessions by keyword |
nox --forcecolor | Force colored output |
Configuration
Basic noxfile.py
import nox
@nox.session
def tests(session):
"""Run the test suite."""
session.install("pytest", "pytest-cov")
session.install(".")
session.run("pytest", "tests/", "-v")
@nox.session
def lint(session):
"""Run linters."""
session.install("ruff")
session.run("ruff", "check", "src/", "tests/")
@nox.session
def typecheck(session):
"""Run type checking."""
session.install("mypy", "types-requests")
session.install(".")
session.run("mypy", "src/")
@nox.session
def docs(session):
"""Build documentation."""
session.install("mkdocs", "mkdocs-material")
session.run("mkdocs", "build")
Parametrized Sessions
import nox
@nox.session(python=["3.10", "3.11", "3.12"])
def tests(session):
"""Run tests across Python versions."""
session.install("pytest", ".")
session.run("pytest")
@nox.parametrize("django", ["4.2", "5.0"])
@nox.session(python=["3.11", "3.12"])
def test_django(session, django):
"""Test against multiple Django versions."""
session.install(f"django=={django}", "pytest", "pytest-django", ".")
session.run("pytest", "tests/")
Reusable Sessions
import nox
def install_deps(session):
"""Helper to install common dependencies."""
session.install("-r", "requirements.txt")
session.install("-r", "requirements-dev.txt")
session.install("-e", ".")
@nox.session
def tests(session):
install_deps(session)
session.run("pytest", *session.posargs)
@nox.session
def coverage(session):
install_deps(session)
session.run("pytest", "--cov=src", "--cov-report=html")
Advanced Usage
Conditional Sessions
import nox
import os
@nox.session
def tests(session):
session.install("pytest", ".")
args = ["pytest"]
if os.environ.get("CI"):
args.extend(["--tb=short", "-q"])
else:
args.extend(["-v", "--tb=long"])
session.run(*args)
@nox.session
def deploy(session):
"""Deploy only on main branch."""
if os.environ.get("GITHUB_REF") != "refs/heads/main":
session.skip("Only deploy from main branch")
session.install("twine")
session.run("twine", "upload", "dist/*")
Session Options
@nox.session(
python="3.12",
reuse_venv=True,
tags=["ci"],
venv_backend="virtualenv",
)
def fast_tests(session):
session.install("pytest", ".")
session.run("pytest", "-x", "--tb=short")
# Use conda instead of venv
@nox.session(venv_backend="conda")
def conda_tests(session):
session.conda_install("numpy", "pandas")
session.install(".")
session.run("pytest")
Tags and Filtering
@nox.session(tags=["lint"])
def ruff(session):
session.install("ruff")
session.run("ruff", "check", ".")
@nox.session(tags=["lint"])
def mypy(session):
session.install("mypy", ".")
session.run("mypy", "src/")
@nox.session(tags=["test"])
def unit(session):
session.install("pytest", ".")
session.run("pytest", "tests/unit/")
@nox.session(tags=["test"])
def integration(session):
session.install("pytest", ".")
session.run("pytest", "tests/integration/")
# Run by tag
nox -t lint
nox -t test
# Run by keyword
nox -k "not integration"
Default Sessions
# Set which sessions run by default
nox.options.sessions = ["tests", "lint"]
nox.options.reuse_existing_virtualenvs = True
nox.options.default_venv_backend = "virtualenv"
Troubleshooting
| Issue | Solution |
|---|---|
| Python version not found | Install it; use nox -s tests-3.12 syntax |
| Session skipped | Check nox -l for available sessions |
| Slow venv creation | Use nox -r to reuse existing environments |
| Import error | Ensure session.install(".") is called |
| posargs not passing | Use nox -- -x -v (args after --) |
# List sessions
nox -l
# Reuse virtualenvs (faster)
nox -r -s tests
# Pass arguments to test runner
nox -s tests -- -x -v tests/test_specific.py
# Force recreate virtualenvs
nox -s tests --forcecolor
# Verbose
nox -v -s tests