8. Making Tests in Containers¶
Lesson Objectives
- Understand why you should test a container.
- Learn how to add a
%testsection to yourdeffile. - Learn how to run a container's tests with
apptainer test. - Understand how tests are run automatically when a container is built.
Questions
- How do I check that a container actually works as intended?
- How can the person who builds a container give users a way to verify it?
When you share a container — with collaborators, or by running it on an HPC like Mahuika — you want some confidence that it actually works before you rely on it. A container that builds successfully will not always run correctly: a package might be missing, a binary might not be on the PATH, or a library might fail to load.
Apptainer lets the creator of a container embed a set of self-tests inside it. Any user can then run those tests with a single command to confirm the container behaves as expected, without needing to know how it was built.
The %test section¶
You add tests to a container by including a %test section in your def file. The commands in this section are run inside the container, and the test passes if the section exits with status 0 and fails if it exits with a non-zero status.
For example, consider a container built to run the fortune, cowsay, and lolcat tools. We can add a %test section that checks each of these programs is present (the full file is available as lolcow.def):
Bootstrap: docker
From: ubuntu:24.04
%post
apt-get -y update
apt-get -y install fortune cowsay lolcat
%environment
export LC_ALL=C
export PATH=/usr/games:$PATH
%runscript
fortune | cowsay | lolcat
%test
echo "Running container self-tests..."
# Helper: check a command exists, fail the test if it does not
check_found () {
if command -v "$1" >/dev/null 2>&1; then
echo "$1 was found"
else
echo "ERROR: $1 was NOT found"
exit 1
fi
}
check_found fortune
check_found cowsay
check_found lolcat
The key detail is the exit 1 — if any program is missing, the test section exits with a non-zero status and Apptainer reports the test as failed.
Running the tests with apptainer test¶
Once the container is built (here, lolcow.sif), anyone can run its tests with:
If everything is in order you will see:
user.name@computer-name:~$ apptainer test lolcow.sif
Running container self-tests...
fortune was found
cowsay was found
lolcat was found
If a test fails, the failing message is printed and the command returns a non-zero exit code, which makes it easy to use in scripts:
user.name@computer-name:~$ apptainer test lolcow.sif
Running container self-tests...
ERROR: lolcat was NOT found
Tests run automatically at build time¶
By default, Apptainer runs the %test section automatically at the end of a build, so a broken container is caught immediately:
You will see the test output appear once the build has finished. If you want to skip the tests during the build (for example, while you are still developing the def file), use the --notest flag:
Writing good tests¶
A useful %test section does more than check that files exist — it confirms the container does what it is meant to do. Some good things to test:
- Programs are installed and on the
PATH(as in the example above). - A program actually runs and produces the expected output, e.g.
python3 -c "import numpy; print(numpy.__version__)". - The correct version is installed, which is important for reproducibility.
- Key data files or directories exist inside the container.
Tip
Keep your tests fast and self-contained. The aim is a quick "does this container work?" check that a user can run before launching a long job on the HPC — not a full test suite.
Exercises¶
For these exercises, we will use the my_python3.12.def file from Chapter 4, which builds a container with Python 3.12.
Question 1
Add a %test section to the my_python3.12.def file that checks python3.12 is installed and available on the PATH.
Question 2
You have just built the container as my_python3.12.sif. How would you run its tests? And how could you build a container without running its tests?
Solution
Run the tests on the built container with:
The tests also run automatically at the end of apptainer build my_python3.12.sif my_python3.12.def. To build without running the tests, use the --notest flag:
Question 3
Checking that python3.12 exists is good, but for reproducibility we often want to confirm the correct version is installed. Write a %test section that fails unless the container's Python is version 3.12.
Solution
We can run python3.12 --version and check its output contains 3.12:
%test
echo "Running container self-tests..."
if python3.12 --version | grep -q "3.12"; then
echo "Python 3.12 was found"
else
echo "ERROR: Python 3.12 was NOT found"
exit 1
fi
Because the test exits with a non-zero status (exit 1) when the version does not match, apptainer test (and the build) will report it as failed.
Keypoints
- Use the
%testsection of adeffile to embed self-tests in a container. - The test passes on exit code
0and fails on any non-zero exit code — useexit 1to flag problems. - Run a container's tests with
apptainer test <container>.sif. - Tests run automatically at the end of a build; skip them with
apptainer build --notest.