Languages that support reflection allow you to mark a function as a test which the test framework then automatically runs. Not having to explicitly run the test is nice. C and C++ don't support runtime reflection so at some level you have to also explicitly run the function. That's not so nice. But... I've an idea. One that I mentioned it to my friend Kevlin Henney and it turns out he was thinking the same kind of thoughts. As usual he was a few steps ahead of me. The idea is don't use functions! Make the basic unit of testing a block instead of a function! For example, instead of having three separate functions naming three separate tests why not have three separate named blocks inside a single function. Something like this:
void test_holder()
{
TEST("first")
{ ... /* assertion */
}
TEST("second")
{ ... /* assertion */
}
TEST("third")
{ ... /* assertion */
}
}
The test framework then calls the function three times, ensuring each block gets run once. How does it know to call the function three times? The idea is to have two phases, a gather phase and a run phase. First set the phase to gather and call the function. In this phase the TEST macro does not execute, instead it inserts itself into a collection of test blocks. When all the test blocks have been gathered switch to run phase and run the test blocks one at a time. Here's a bare bones implementation of the idea (in C99)
#include <assert.h>
#include <stdbool.h>
#define TEST(name) if (run_after_gather(blocks, #name))
#define IGNORE(name) if (ignore())
struct test_blocks
{
int size;
const char * names[1024];
bool in_gather_phase;
const char * run_name;
};
bool run_after_gather(struct test_blocks * blocks, const char * name)
{
if (blocks->in_gather_phase)
{
blocks->names[blocks->size] = name;
blocks->size++;
return false;
}
else
return blocks->run_name == name;
}
bool ignore(void) { return false; }
void run(void (*test)(struct test_blocks *))
{
struct test_blocks blocks = { .size = 0 };
blocks.in_gather_phase = true;
test(&blocks);
blocks.in_gather_phase = false;
for (int at = 0; at != blocks.size; at++)
{
blocks.run_name = blocks.names[at];
test(&blocks);
}
}
/* - - - - - - - - - - - - - */
void tests(struct test_blocks * blocks)
{
TEST(first)
{
assert(1==1);
}
IGNORE(second)
{
assert(2==21);
}
TEST(third and last)
{
assert(3==31);
}
}
int main()
{
run(tests);
return 0;
}