Testing
Last updated on 2026-10-07 | Edit this page
Overview
Questions
- Why have unit tests?
Objectives
- Self documenting Makefile
- Use pytest for unit tests
- Use coverage to check test coverage
Use the following commands if you want to save your work from the previous episode
Now use the following to get files for this episode
which should yield
OUTPUT
Makefile
books/abyss.txt
books/isles.txt
src/TeX/local.bib
src/TeX/report.tex
src/analysis/countwords.py
src/analysis/testzipf.py
src/plotscripts/plotcounts.py
This episode is primarily about testing, but first we will make the Makefile self documenting. Try
OUTPUT
help : Holy mackerel! What is this?
OUTPUT
head -n 10 $< |awk '{print $$1, $$2}' > $@
.PHONY : clean
clean :
rm -rf build
## help : Holy mackerel! What is this?
.PHONY : help
help : Makefile
@sed -n 's/^## / /p' $<
Self Documenting Makefiles
Explain how the help block at the end of the Makefile works and how you could use it to document the targets build/report.pdf, build/results.tex, build/abyss.head, clean, and help.
The sed command simply prints each line in the Makefile that starts with “##”. Now we will edit the file to document each of those targets.
So far we have tested each incremental change of our code with something like
While that takes less than three seconds to redo all the calculations and build the final document, our toy research project is a surrogate for projects with calculations and other steps that take hours if not days. For such a project one can use unit tests to test each chunk of code quickly in isolation.
Unit Tests
Google AI says:
Unit tests are automated pieces of code written to verify that a small, isolated part of a software application, usually a single function or method, behaves as expected.
Pytest
We will use the utility pytest to run unit tests, and we will put the testing code in the directory tests/. Here is the block required in the Makefile:
## test : Run pytest on tests/
.PHONY : test
test :
pytest tests/
And here is the code for tests/test_countwords.py
PYTHON
"""test_countwords.py
debug with
$py.test --pdb tests/test_countwords.py
"""
import pytest
import pathlib
import analysis.countwords
@pytest.fixture(scope="function")
def shared_count_file(tmp_path_factory):
# 1. Create a temporary directory using the factory
temp_dir = tmp_path_factory.mktemp("shared_data")
# 2. Create the file inside that directory
count_file = temp_dir / "word_counts"
count_file.write_text('{"status": "ready"}')
return count_file
def test_word_count(shared_count_file):
"""Check reading books/abyss.txt
"""
in_path = 'books/abyss.txt'
analysis.countwords.word_count(in_path, shared_count_file)
def test_main(shared_count_file: pathlib.PosixPath):
in_path = 'books/abyss.txt'
analysis.countwords.main([in_path, f'{shared_count_file}'])
After making those changes to the project we can test testing with
pytest tests/
================================ test session starts ================================
platform linux -- Python 3.12.12, pytest-8.3.5, pluggy-1.5.0
rootdir: /mnt/precious/home/andy_nix/projects/SIAMDS27/learner_setup
plugins: anyio-4.9.0, cov-6.1.0, dependency-0.6.0
collected 2 items
tests/test_countwords.py .. [100%]
================================= 2 passed in 0.10s =================================
Next we introduce the utility coverage that reports on the sections of code that are covered by a collection of unit tests.
coverage.py
Google AI says:
Coverage.py is the standard, authoritative code coverage measurement tool for Python. […] it monitors your program during execution to track which lines of code have been run and which have not. It is primarily used to gauge the effectiveness and completeness of your test suites.
Here is a block for the Makefile
## coverage : make test coverage report in build/htmlcov/
.PHONY : coverage
coverage :
rm -rf .coverage build/htmlcov
coverage run --source tests,src/analysis,src/plotscripts -m pytest tests
coverage html -d build/htmlcov
@echo To view: "firefox build/htmlcov/index.html & "
Let’s test that with
OUTPUT
rm -rf .coverage build/htmlcovcoverage run --source tests,src/analysis,src/plotscripts -m pytest tests
================================ test session starts ================================
platform linux -- Python 3.12.12, pytest-8.3.5, pluggy-1.5.0
rootdir: /mnt/precious/home/andy_nix/projects/SIAMDS27/learner_setup
plugins: anyio-4.9.0, cov-6.1.0, dependency-0.6.0
collected 2 items
tests/test_countwords.py .. [100%]
================================= 2 passed in 0.16s =================================
coverage html -d build/htmlcov
Wrote HTML report to build/htmlcov/index.html
To view: firefox build/htmlcov/index.html &
After looking at the result in a browser, we see that we should write tests for testzipf.py and plotcounts.py. We have written those and included them in the git branch 05-standards for the next episode.
- Use unit tests for finding bugs quickly, and checking changes.
- For Python code pytest works
- Use a coverage tool for finding code that the testing suit missed
- For Python code coverage works