Summary and Schedule
In science, we rely on establishing the behaviour and accuracy of experimental devices before making actual measurements, for example through calibration. Equally, theoretical calculations make predictions that inform experiment design and data analysis decisions, and thus we also rely on establishing the correctness of these calculations. The same is true of the software we use to simulate, take, and analyse the data collected through experiments - it is an theoretical calculation or experimental device just as much as any other. We must therefore throughly test our software’s accuracy and reliability in order that the scientific results we derive from its use are not compromised.
In this lesson, we introduce methods and tooling for unit testing C++ code. Our “units” here are the lower level components of the code, specifically individual functions and/or classes. By defining test cases, test suites and test fixtures, we’ll see how we can integrate testing as a standard part of the software development workflow. The GoogleTest framework will be introduced to build these elements, a commonly used testing system in both science and industry that saves us a lot of otherwise boilerplate coding. We’ll finish by looking at how unit testing provides a foundation for higher level integration and regression testing, and other useful tooling for aspects unit testing cannot catch.
| Setup Instructions | Download files required for the lesson | |
| Duration: 00h 00m | 1. What is unit testing? |
How do I know my code is correct? What is a unit test and how does it relate to how I already think about correctness? |
| Duration: 00h 12m | 2. Organizing code to enable unit testing |
How should we structure C++ code to assist unit testing? What makes a function easy or hard to test? |
| Duration: 00h 24m |
3. Unit testing with
assert()
|
How can we implement unit tests in C++? What are the limitations of using assert() for testing?
|
| Duration: 00h 36m | 4. Integrating tests into a build system |
How do I build and run my tests automatically? How does a build system benefit testing as a project grows? |
| Duration: 00h 48m | 5. Introducing GoogleTest |
What are the remaining limitations of assert() that prevent
it scaling to a real test suite?How does a testing framework address those limitations? |
| Duration: 01h 00m | 6. Floating point comparisons |
Why does EXPECT_EQ fail for values I believe are
correct?How do I test numerical code reliably? |
| Duration: 01h 12m | 7. Testing exceptional behaviour |
How do I verify that my code fails in the right way? What should happen when a function receives invalid input? |
| Duration: 01h 24m | 8. Testing stateful classes |
How do I test code that has to be constructed and populated before I can
interrogate it? How do I verify results that are collections rather than single values? :::::::::::::::::::::::::::::::::::::::::::::::: |
| Duration: 01h 36m | 9. Test fixtures |
I am writing the same setup code in every test — is there a better
way? How do I share a complex starting state across many tests without tests interfering with each other? |
| Duration: 01h 48m | 10. Code coverage |
How do I know which parts of my code my tests actually
exercise? What does test coverage tell me, and what doesn’t it tell me? |
| Duration: 02h 00m | 11. Sanitizers as another line of defence |
The tests all pass — so why does the program crash? What classes of bug are invisible to unit tests? |
| Duration: 02h 12m | Finish |
The actual schedule may vary slightly depending on the topics and exercises chosen by the instructor.
Software installation
These instructions set out how to obtain and install the software on Linux or macOS. It is assumed that you have:
- access to the Bash or Zsh shell on a fairly modern Linux or macOS system
- a text editor such as Vim or Emacs, or an IDE such as VSCode
- sufficient disk space (~300MB) to store the software and outputs
You do not need root/administrator access.
C++ Compiler
As we will be coding in C++ (and the C++17 standard), we will need a compiler. We’ll take this from the system for reliability, so it can be installed through the appropriate package management system.
Install GCC through your system’s package manager.
Install Xcode from the App Store.
This lesson has not been designed to run on Windows. We would recommend using the Windows Subsystem for Linux, and following the instructions for Linux in the rest of the lesson.
Pixi
As we move through the course, we will need additional tools to help us write, build, and run our tests efficiently. To do this, we will use the Pixi package manager to provide an isolated development environment with the software we need.
Open a terminal and run:
or, if you don’t have curl available:
More details on what these do are on Pixi’s installation page.
If you have Homebrew installed, then it’s recommended to install Pixi with this. Open a terminal and run:
Otherwise, you should install Pixi directly:
More details on what these do are on Pixi’s installation page.
Why Pixi?
If you’re familiar with installing software, you might be asking why we’re using Pixi, another package manager, on top of the system ones (or similar tools like Homebrew). The answer is in two parts:
- Consistency: we can lock in the versions of the tools used to avoid issues in the exercises
- Simplified workflow: We’ll use Pixi’s tasks to simplify the configure/build/test workflow so we can concentrate on the testing concepts rather the tooling.
It is also very similar in concept to (and uses packages from) Conda which you may be familiar with already.
Software Setup
Open a terminal and check that you have the following commands available:
and
Download the project zip file
and unzip it somewhere suitable. Change into the the unpacked
ccptest-test/ base directory and try setting up the Pixi
environment (NB: we have shown $, the prompt, to
distinguish the commands to be run from the output you should see. You
don’t need to type this!):
BASH
$ cd ccptest-test
$ ls
CMakeLists.txt pixi.lock README.md test
LICENSE.md pixi.toml src
$ pixi shell
This will download the packages needed and drop you into a shell with the environment setup to use these. Depending on your network connection and system, this may take several seconds. You should see your prompt changed to reflect this new environment:
To check you have the tools needed, run the following commands and check they complete with the following information:
BASH
(ccptepp-test) $ cmake --version
cmake version 4.2.0
CMake suite maintained and supported by Kitware (kitware.com/cmake).
(ccptepp-test) $ ninja --version
1.13.2
(ccptepp-test) $ gcovr --version
gcovr 8.2
Copyright (c) 2013-2024 the gcovr authors
Copyright (c) 2013 Sandia Corporation.
Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
the U.S. Government retains certain rights in this software.
To exit the Pixi environment, use exit to get back to
your default terminal environment:
BASH
(ccptepp-test) $ exit
Saving session...
...saving history...truncating history files...
...completed.
$
You can also simply close the terminal.
More detailed information on Pixi use will be provided in each
exercise as needed, though in all cases we will be working in the
directory ccptepp-test/.