4 Unit tests
This blog post explains the interest of unit testing when developing a Python package. It’s not meant as an in-depth tutorial about how to do unit tests, but rather an overview of what problem it solves and how it is relevant to Python packaging.
If you want to learn how to do unit testing, the best place to start is the official documentation.
What is unit testing
Unit testing is the practice of writing small, focused tests to verify that individual parts (units) of your code behave as expected. In the context of a Python package, unit tests help ensure that your functions, classes, and modules continue to work correctly as your code evolves.
Quick definition
A unit is the smallest testable part of your application, typically a single function. A unit test checks if that function produces the correct output for a given input. These tests are automated and are typically run frequently during development.
Pytest
Pytest is a popular testing framework in the Python ecosystem. It’s known for its clean syntax, powerful features (like fixtures and parameterization), and ease of use. Pytest works with simple assert statements, so you don’t need to learn a special API to start testing.
Example with a simple function
Let’s look at how unit tests work using a basic example. We assume that we have a package named sunflower:
sunflower/
├── sunflower/
│ └── __init__.py
└── pyproject.tomlSimple function
Suppose you have a function that counts how many times the word “sunflower” appears in a string:
sunflower/count_sunflower_module.py
import re
def count_sunflowers(s):
s = re.sub(r"[^a-zA-Z\s]", "", s) # Remove non-text characters
s = s.lower() # Convert to lowercase
n_sunflower = s.split().count("sunflower")
n_sunflowers = s.split().count("sunflowers")
return n_sunflower + n_sunflowersSimple test example and to run it
A test is actually a function (or a class) that calls the function we want to test. This new function must
- start with
test_ - be in a file in the
tests/directory in a file that starts withtest
For instance, our project could be organized as follows:
sunflower/
├── sunflower/
│ ├── __init__.py
│ └── count_sunflower_module.py
├── tests/
│ └── test_count_sunflower.py
└── pyproject.tomlOur test function will verify if our original function behaves as expected. For example, we can test count_sunflowers() with the following test:
tests/test_count_sunflower.py
import pytest
from sunflower import count_sunflowers
def test_count_sunflowers():
output = count_sunflowers("sunflower sunflower cake")
expected = 2
assert output == expectedTo run the test, simply use the pytest command in the terminal:
pytestIf the result of assert output == expected is True, then pytest will tell us that everything’s fine, otherwise an error message with details on where the error comes from for debugging.
In real projects
Unit testing becomes even more valuable as your project grows and others start using or contributing to it.
Backward compatibility
Tests protect against accidental changes that could break existing behavior. If you modify a function, running your tests will quickly show if the new version still satisfies the old expectations.
For example, let’s say we modify our count_sunflowers() function in order to make it 2x faster. This change should not make anything different from the user point of view, it should just be faster.
CI
Unit tests are often run automatically using Continuous Integration (CI) tools like GitHub Actions. Whenever someone pushes a commit or opens a pull request, the CI system runs the test suite to make sure nothing is broken. This reinforces trust in the codebase and speeds up development.
There is a dedicated blog post that covers GitHub Actions in more detail.
Collaborative work and external contributions
Having a robust test suite helps contributors understand what your code is supposed to do. It also encourages better code quality and smoother collaboration, since developers can make changes confidently and verify them with tests.
To learn more about how and why to write unit tests, check out the official documentation.