DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Supercharging Pytest: Integration With External Tools
  • Mastering Async Context Manager Mocking in Python Tests
  • Exploring Playwright’s Feature “Copy Prompt”
  • Mastering Redirects With Cloudflare Bulk Redirects

Trending

  • Navigating Change Management: A Guide for Engineers
  • Using Python Libraries in Java
  • Manual Sharding in PostgreSQL: A Step-by-Step Implementation Guide
  • Can You Run a MariaDB Cluster on a $150 Kubernetes Lab? I Gave It a Shot
  1. DZone
  2. Coding
  3. Tools
  4. Automating Python Multi-Version Testing With Tox, Nox and CI/CD

Automating Python Multi-Version Testing With Tox, Nox and CI/CD

Automate Python testing across multiple versions with Tox and Nox. Tox offers declarative testing, Nox provides flexibility, and both can be run with CI/CD pipelines.

By 
Wallace Espindola user avatar
Wallace Espindola
·
Dec. 26, 24 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
3.5K Views

Join the DZone community and get the full member experience.

Join For Free

In modern Python development, maintaining compatibility across multiple Python versions is super critical, especially for libraries and tools that target a diverse user base. Here, we explore how to use Tox and Nox, two powerful tools for Python test automation, to validate projects across multiple Python versions. Using a concrete project as an example, we’ll walk through setting up multi-version testing, managing dependencies with Poetry, and using Pytest for unit testing. CI/CD is used for DevOps automation.

Why Automate Multi-Version Testing?

Automating tests across Python versions ensures your project remains robust and reliable in diverse environments. Multi-version testing can:

  • Catch compatibility issues early.
  • Provide confidence to users that your package works on their chosen Python version.
  • Streamline the development process by reducing manual testing.

What Are Tox, Nox, and CI/CD?

Tox

Tox is a well-established tool designed to automate testing in isolated environments for multiple Python versions. It is declarative, with configurations written in an ini file, making it easy to set up for standard workflows.

Nox

Nox is a more flexible alternative that uses Python scripts for configuration, allowing dynamic and programmable workflows. It is based on a python code config file, so it is more robust than Tox to complex project configurations.

Continuous Integration and Continuous Deployment (CI/CD)

CI/CD is a set of practices and tools designed to streamline and automate the software development lifecycle. Continuous Integration (CI) focuses on automatically building and testing code changes as they are integrated into a shared repository, ensuring that new commits do not introduce defects and that the codebase remains stable. Continuous Deployment (CD) extends this automation by automatically deploying validated code to production or staging environments, facilitating rapid and reliable delivery of features and updates. By implementing CI/CD pipelines, teams can enhance collaboration, reduce integration issues, accelerate release cycles, and maintain high code quality, ultimately enabling more efficient and resilient software development processes.

Those tools aim to simplify testing across multiple versions, but their approaches make them suited for different needs.

Project Setup: A Practical Example

Let’s set up a project named tox-nox-python-tests to demonstrate the integration of Tox, Nox, Poetry, and Pytest. The project includes simple addition and subtraction functions, tested across Python versions 3.8 to 3.13.

1. Directory Structure

Here’s how the project is structured:

Plain Text
 
tox-nox-python-tests/
├── .github
│   └── workflows
│      └── python-tests-matrix.yml
├── tox_nox_python_test_automation/
│   ├── __init__.py
│   ├── main.py
│   └── calculator.py
├── tests/
│   ├── __init__.py
│   └── test_calculator.py
├── .gitlab-ci.yml
├── pyproject.toml
├── tox.ini
├── noxfile.py
└── README.md


2. Core Functionality

The calculator module contains two basic functions:

Python
 
calculator.py

def add(a, b):
    """Returns the sum of two numbers."""
    return a + b

def subtract(a, b):
    """Returns the difference of two numbers."""
    return a - b


We will use pytest for unit testing (with parametrized tests):

Python
 
test_calculator.py

import pytest
from tox_nox_python_tests.calculator import add, subtract

@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (-1, 1, 0),
    (0, 0, 0),
])
def test_add(a, b, expected):
    assert add(a, b) == expected

@pytest.mark.parametrize("a, b, expected", [
    (5, 3, 2),
    (10, 5, 5),
    (-1, -1, 0),
])
def test_subtract(a, b, expected):
    assert subtract(a, b) == expected


3. Dependency Management With Poetry

Poetry manages dependencies and environments. The pyproject.toml file is the modern way to manage Python projects and replaces traditional setup.py and setup.cfg files:

TOML
 
pyproject.toml

[tool.poetry]
name = "tox_nox_python_tests"
version = "0.1.0"
description = "Automate Python testing across multiple Python versions using Tox and Nox."
authors = ["Wallace Espindola <wallace.espindola@gmail.com>"]
readme = "README.md"
license = "MIT"

[tool.poetry.dependencies]
python = "^3.8"
pytest = "^8.3"

[build-system]
requires = ["poetry-core>=1.8.0"]
build-backend = "poetry.core.masonry.api"


Run the following commands to install dependencies and create a virtual environment:

Shell
 
poetry install


4. Running Unit Tests With Pytest

 You can run the basic unit tests this way:

Shell
 
poetry run pytest --verbose


5. Multi-Version Testing With Tox

Tox automates testing across Python versions using isolated virtual environments. The configuration is declarative and resides in a tox.ini file, testing the app with Python versions from 3.8 to 3.13:

Properties files
 
tox.ini

[tox]
envlist = py38, py39, py310, py311, py312, py313

[testenv]
allowlist_externals = poetry
commands_pre =
    poetry install --no-interaction --no-root
commands =
    poetry run pytest --verbose


Run Tox using:

Shell
 
poetry run tox


Tox will automatically create environments for each Python version and execute the pytest suite in isolation.

Example output:

Tox will automatically create environments for each Python version and execute the pytest suite in isolation

6. Flexible Testing With Nox

For more dynamic configurations, use Nox. Its Python-based scripts allow complex logic and custom workflows and test the app with Python versions from 3.8 to 3.13:


Python
 
noxfile.py

import nox

@nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"])
def tests(session):
    session.install("poetry")
    session.run("poetry", "install", "--no-interaction", "--no-root")
    session.run("pytest")


Run Nox using:

Shell
 
poetry run nox


Nox offers flexibility for customizing workflows, such as conditional dependency installation or environment-specific configurations.

Example output:


7. Integration With CI/CD

Automate the testing process further by integrating Tox and/or Nox into a CI/CD pipeline. 

For example, a GitHub Actions workflow file .github/workflows/python-tests.yml can look like this for Tox or Nox tests:

YAML
 
name: Python Tests Tox (or Nox)

on:
  push:
    branches: [ main, develop ]

jobs:
  test:
    runs-on: ubuntu-latest
   
    steps:
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: ${PYTHON_VERSION}
          
      - name: Install Poetry
        run: pip install poetry
        
      - name: Install Dependencies
        run: poetry install --no-interaction --no-root
        
      - name: Run Tests with Tox (or Nox)
        run: poetry run tox (or nox)


And it can look like this for parallel matrix tests (pure CI/CD):

YAML
 
name: Python Tests with Matrix

on:
  push:
    branches: [ main, develop ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        python-version: [3.8, 3.9, 3.10, 3.11, 3.12, 3.13]
    
    steps:
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          
      - name: Install Poetry
        run: pip install poetry
        
      - name: Install Dependencies
        run: poetry install --no-interaction --no-root
        
      - name: Run Tests with Pytest
        run: poetry run pytest --verbose


As an option, the following GitLab CI/CD pipeline .gitlab-ci.yml runs tests with Tox or Nox (keep in mind that you run either Tox or Nox in your project, not both, since they have similar goals):

YAML
 
stages:
  - test

tox_tests:
  stage: test
  image: python:${PYTHON_VERSION}
  variables:
    PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  before_script:
    - pip install poetry
    - poetry install --no-interaction --no-root
  script:
    - poetry run tox (or nox)
  cache:
    paths:
      - .cache/pip/


Also, this one runs the tests across multiple versions with a parallel matrix (pure CI/CD):

YAML
 
stages:
  - test

matrix_tests:
  stage: test
  image: python:${PYTHON_VERSION}
  variables:
    PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  before_script:
    - pip install poetry
    - poetry install --no-interaction --no-root
  script:
    - poetry run pytest --verbose
  cache:
    paths:
      - .cache/pip/
  parallel:
    matrix:
      - PYTHON_VERSION: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]


This configuration uses the matrix keyword to run tests across specified Python versions in parallel. 

Conclusion

By combining Tox or Nox with Poetry and Pytest, you can achieve seamless multi-version test automation for Python projects. Whether you prefer the declarative approach of Tox or the programmable flexibility of Nox, these tools ensure your code is validated across a wide range of Python environments. That approach is especially useful for shared libs projects and multi-user environments projects. Also, for a full DevOps approach, you can use CI/CD matrix tests for multiple versions, a powerful and clean solution.

For the complete example, check the project repository GitHub: tox-nox-python-tests.

For other engaging tech discussion, follow me on my LinkedIn.

References

This project uses Tox, Nox, CI/CD, Poetry, and Pytest for test automation. For detailed documentation, refer to:

  • Tox Documentation
  • Nox Documentation
  • Poetry Documentation
  • Pytest Documentation
  • GitLab CI/CD Documentation
  • GitHub Actions CI/CD Documentation
Tool Nox (platform) Python (language) Testing

Opinions expressed by DZone contributors are their own.

Related

  • Supercharging Pytest: Integration With External Tools
  • Mastering Async Context Manager Mocking in Python Tests
  • Exploring Playwright’s Feature “Copy Prompt”
  • Mastering Redirects With Cloudflare Bulk Redirects

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!