I regularly advocate for the importance of unit tests (and all tests in general). LibCapy of course has its own unit tests, but I'm currently not using a particular tool or framework for that. Consequently even if they deserve their purpose, they are dirty and need to be improved and cleaned up to bring LibCapy nearer to an hypothetical version 1.0. A first step was to create a tool (you weren't expecting I would not do it myself, right?) to simplify their implementation and execution, which I'm introducing and sharing in this article.
To improve over my current unit tests implementation, all I needed for now was a tool that would allow it to be shorten to the definition of unit tests function. The execution would be automated, as well as a nice and clean output. Here again I've used C macros to accomplish it in a super easy and straightforward way. It's done in less than a hundred lines and a short afternoon (incuding this article). It includes one unit test file and executes the functions defined in it. The only fancy trick consists of redefining macros so that the single definition of the user can be reused differently several time, and hide code behind some if(0) when need: once as it is to define the unit test functions, once to count the number unit test functions before executing them, and once to call them without their body interfering. The rest is just producing clean output for the user.
It's used as follow. Gather your unit tests into one or several files where each test function is defined with the macro CUTEST(testName, "Test description") { ... }. Ah, CUTest, that's the name of the tool, because "C Unit Test", and that's cute ! testName is the name of the unit test function, Test description is the descrition displayed the execution. The body of the function is the definition of the unit test. If you want, you can add other functions (for your fixtures typically), as long as you include-guard them. Only functions that are defined with CUTEST() will be considered as unit tests. You will almost certainly also need to include the module header of what you're testing. Inside the unit test body, the test condition is performed using another macro: CUTEST_ASSERT(cond, msg, msg_arg1, msg_arg2, ...). cond is the condition to be satisfied for the unit test to pass (you may have several one per test if you want). msg, msg_arg1, ... are the arguments passed to a 'printf' call in case the condition is not satisfy with the view to explain why it wasn't.
An example unit test file would look like this:
That's all for the unit tests file creation. The execution is illustrated by the Makefile below, given that you've put all your unit test files in the UnitTests folder and they're named test_XXX.c. You'll probably need to modify the compilation command with compilation argument needed by your project.
This will execute all the unit test functions in all the unit test files and print a summary looking as follow:
The code of CUTest is as follow (you can download the source by clicking here):
(Edited on 2022/10/11 to improve the output when there is several assert in the same test)
(Edited on 2022/11/22 to display the currently running test's label without waiting its completion)
(Edited on 2022/11/23 to correct bug when splitting long labels)
(Edited on 2023/12/10 to add the command line option for executing only one single test)
(Edited on 2023/12/12 to refactor 'unsigned int' into 'size_t')
(Edited on 2024/01/25 to correct a formatting bug when a label has a size such as it ends exactly at the end of the line)
Now that I have the tool, I just have to refactor all the unit tests done so far ! (^_^;)
Edit on 2022/11/21: when using CUTest with LibCapy, the version below should be preferred as it properly handles uncaught exceptions (you can download the source by clicking here):