Search code examples
cunit-testinggcctddbdd

Multiple Describe() in cgreen


I am using cgreen to write tests for my C code, my question is:

Short version: Is it possible to put more than one Describe() in one file?

Long version: I have different test files, with their own Describe(), BeforeEach() and AfterEach(). Since in these files, I've used same module, I have to compile them together. [for preventing compiler from including the module more than once, I used this trick (include guard)

#ifndef SOME_NAME
#define SOME_NAME

...

#endif

I also have a file which contains all my tests, let say all_tests.c. Now, I want to include my test files into all_tests.c and compile everything together, something like this:

#include <cgreen/cgreen.h>

#include "./test1.c"
#include "./test2.c"

int main() {
    TestSuite *suite = create_test_suite();

    add_suite(suite, test1_tests());
    add_suite(suite, test2_tests());

    return run_test_suite(suite, create_text_reporter());
}

which leads to these errors:

error: redefinition of ‘setup’
error: redefinition of ‘teardown’

Obviously, because there is multiple definition of Describe(), BeforeEach() and AfterEach(). Well, I couldn't find a better idea, using recommended way of

TestSuite *test1_tests();
TestSuite *test2_tests();

instead of including the files directly and assembling and compiling each test file separately and then linking all file together leads to this error:

multiple definition of `some_global_module'

which is expected, as in the linking state, there is multiple definition of the some_global_module. Just for being clear, the test1.c file looks like this (and also the test2.c file, just change test1 to test2 in the following code) :

#include <cgreen/cgreen.h>

#include "./some_global_module.h"
#include "./some_other_module_i_want_to_test1.h"

Describe(test1);

BeforeEach(test1) {
    ...
}

AfterEach(test1) {
    ...
}

Ensure(test1, do_something) {
    ...
}

TestSuite *test1_tests() {
    TestSuite *suite = create_test_suite();
    add_test_with_context(suite, test1, do_something);
    return suite;
}

Any idea? Perhaps there is some compiler trick that I can use or an easier way to manage the whole thing?


Solution

  • There is no way to include multiple Describe() in the same test file. And it should not be necessary.

    You don't explain exactly how you assemble your tests/modules. There are a couple of options:

    1. #include your module source in the test file - this is possible, but then you obviously can't link multiple test files together, you need to run them separately, since they define the same symbols for all in the module under test.

    2. let the test "modules" use your module under test in the same fashion like other users of it would (\#include and calling the public interface). In this case your test files are modules as any other and should be linked together with a single instance of your module under test.

    3. using the cgreen-runner. It loads shared libraries and discovers all tests in the library, alleviating you from the burdon of remembering to add each new test case to the suite.

    Use #1 only if you are retrofitting tests to existing, un-testable, code, or really need to test the internals, like static functions.

    #2 is pretty much standard and probably what you should do. In this case remove any #include "some_global_module.c" from your testfiles, like you have in your test1.c. Your Makefile should look something like:

    all: all_tests
    
    all_tests: all_tests.o some_global_module.o \
        test1.o some_global_module_under_test1.o \
        test2.o some_global_module_under_test2.o
    

    So with test1.c and test2.c looking like you indicate, and the above Makefile content, I fail to see why you should get "multiple definition" at link time.

    #3 is my favourite. Link everything into a shared object library (.so on Linux) and run cgreen-runner on it. Your Makefile should then look something like:

    all: all_tests.so
        cgreen-runner ./$^ 
    
    all_tests.so: some_global_module.o \
            test1.o some_global_module_under_test1.o \
            test2.o some_global_module_under_test2.o
        $(LINK) -shared -o $@ $^
    

    NOTE: if your some_global_module_under_test1 is dependent on a lot of other modules you either need to link them too, or mock them.