Writing unit tests is considered an integral aspect of software development. So much so that most modern programming languages ship with some in-house testing pipeline — be it Python’s unittest
module or Go’s testing
package. In addition to great library functions for testing, they have excellent command-line tools as well. But one of their most defining features is that they’re ridiculously easy to use. That is a valuable attribute a language gains with support for first-class testing. But C++ is a decades-old language and thus there exist no standard libraries for unit testing.
That’s not to falsely signal at a lack of testing libraries for C++. Rather, Google’s googletest library is probably the most popular unit testing framework out there. But as mentioned above, there is a stark lack of easy to use testing libraries. This prompted me to develop my own testing library called shipyard.
It is a header-only library that provides several custom Assert
functions and similar derivatives alongside a generic runner function that sorts functions alphabetically and runs them.
This post is meant to be a tutorial for Shipyard that introduces writing tests, custom Assert
s and using the ship CLI tool that comes with Shipyard for automated test discovery and code generation. Let’s start with a simple prime library that we wish to test. So we’ll start off with a C++ file called primelib.cpp
inside of our root directory.
Simple and clean and basic functions for a prime number library. So far, so good. Now let’s add a file called test_primelib.cpp
—
Let’s walk through the new file. We first include the only Shipyard dependency we need — a single header file. Then we include the file (or header) that contains the functions we wish to test. Next, we define our functions. The Shipyard runner is meant to be used for special test functions that require no arguments and do not have a return. In template speak, it only accepts std::function<void(void)>
.
Shipyard comes with a number of Assert
s for checking equality and we use them to check for the output of the functions we wish to test.
There is no meta macro or templating programming going underneath for Assert
s or naming conventions but Shipyard follows the standard of naming test files with the format — test_<name>.cpp
and having the name of each test for function as test_<function_name>
followed by a suffix containing more information or just a number; for instance, test_is_prime_even_nums
or test_is_prime2
.
This convention is highly recommended since it is used by ship
— a command-line code generator for Shipyard. It scans a provided directory, or the current one if none, and looks for C++ files with the name prefix test_
and inside of them, searches for functions that have the same prefix. Now, in this directory run the ship.py
script and let it generate the actual runner.
$ ship.py -v
Pass the -v
flag for verbosity and it should generate the following code.
And that is it! You can now compile test.cpp
with the appropriate flags although none are needed in this case and it should work.
Now for a few more things — to describe your own Assert
function for a custom type, all you need to do is create a function with a name corresponding to Assert<class_name>
followed by an optional class name (or some other verb you wish to assert) or simply Assert
and overloading will take care of calling with the apt arguments. For example, you have a class called Person that has a family attribute. You want to create an Assert
function for objects of Person with the same family.
Or something similar.
To view a reference of all the Assert
functions provided you can read the reference documentation over at the repository.