Automate your Python code tests with tox

Learn more about solving common Python problems in our series covering seven PyPI libraries.
181 readers like this.
CICD with gears

opensource.com

Python is one of the most popular programming languages in use today—and for good reasons: it's open source, it has a wide range of uses (such as web programming, business applications, games, scientific programming, and much more), and it has a vibrant and dedicated community supporting it. This community is the reason we have such a large, diverse range of software packages available in the Python Package Index (PyPI) to extend and improve Python and solve the inevitable glitches that crop up.

In this series, we'll look at seven PyPI libraries that can help you solve common Python problems. Today, we'll examine tox, a tool for automating tests on Python code.

tox

When writing Python code, it is good to have automated checks. While you could dump the rules for running the checks directly into the continuous integration (CI) environment, that's seldom the best place for it. Among other things, it is useful to run tests locally, using the same parameters the CI runs, to save CI time.

The tox project is designed to run different checks against different versions of Python and against different versions of dependencies. Very quickly, we find the limiting factor is not the flexibility of tox but the harsh realities of the combinatorial explosions of options!

For example, a simple tox configuration can run the same tests against several versions of Python.

[tox]
envlist = py36,py37
[testenv]
deps =
    pytest
commands =
    pytest mylibrary

Tox will automatically use the right version of the interpreter, based on the version of the environment, to create the virtual environment. Tox will automatically rebuild the virtual environment if it is missing or if the dependencies change.

It is possible to explicitly indicate the Python version in an environment.

[tox]
envlist = py36,py37,docs
[testenv]
deps =
    pytest
commands =
    pytest mylibrary
[testenv:docs]
changedir = docs
deps =
    sphinx
commands =
    sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
basepython = python3.7

This example uses Sphinx to build documentation for the library. One nice thing is that the Sphinx library will be installed only in the docs virtual environment. If mylibrary imports on Sphinx but forgets to indicate an explicit dependency, the tests will, correctly, fail.

We can also use tox to run the tests with different versions of the dependencies.

[tox]
envlist = {py36,py37}-{minimum,current}
[testenv]
deps =
  minimum: thirdparty==1.0
  current: thirdparty
  pytest
commands =
    pytest mylibrary

This will run four different test runs: py36-minimum, py36-current, py37-minimum, and py37-current. This is useful in the case where our library depends on thirdparty>=1.0: every test run makes sure we are still compatible with the 1.0 version while also making sure the latest version does not break us.

It is also a good idea to run a linter in tox. For example, running Black will do the right thing.

[tox]
envlist = py36,py37,py36-black
[testenv]
deps =
    pytest
commands =
    pytest mylibrary
[testenv:py36-black]
deps =
    black
commands =
    black --check --diff mylibrary

By default, tox will run all test environments. But you can run just one environment; for example, if you only want to run Black, run tox -e py36-black.

If you have a Python library you care about, add tox.ini to your workflow to keep its quality high.

In the next article in this series, we'll look at flake8, a linter and linting platform that ensures consistency in Python code.

Review the previous articles in this series:

What to read next
Tags
Moshe sitting down, head slightly to the side. His t-shirt has Guardians of the Galaxy silhoutes against a background of sound visualization bars.
Moshe has been involved in the Linux community since 1998, helping in Linux "installation parties". He has been programming Python since 1999, and has contributed to the core Python interpreter. Moshe has been a DevOps/SRE since before those terms existed, caring deeply about software reliability, build reproducibility and other such things.

Comments are closed.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.