1

As a little personal project I am doing a small, very simple, test-framework in C.

The problem I am facing, is that I want to be able to register test cases (i.e. functions with ASSERTS of some kinds in them) automatically when a user writes them, and then run them in my predefined main function.

So from the user side, I only want to have to do

TEST_CASE("TestName1")
{
    // Testing stuff here
}

TEST_CASE("TestName2")
{
     // Testing more stuff here
}

and the rest should be covered.

At the moment the define only looks like

#define TEST_CASE(name) int name()

and the question is therefor if it is possible to somewhat "remember" these functions so I can call them in the main function.

EDIT: So the point is that the user should include a "test-framework.h" which defines a predefined main() function which runs all the tests that the user specified with TEST_CASEs. That meaning it should look something like this:

User defined file:

// File where tests are defined
#include "framework.h"

TEST_CASE("TestCase1")
{
    // Test stuff
}

TEST_CASE("TestCase2")
{
    // Test more stuff
}

And in the framework file:

// framework.h

#define TEST_CASE(name) int name()

int main()
{
    // Here run all TEST_CASEs there is
}

That meaning, I do not know the names of the TEST_CASES in my main().

pingul
  • 3,351
  • 3
  • 25
  • 43
  • Who writes *// Testing stuff here* part? – this Apr 29 '14 at 13:09
  • Ah, might be a little unclear. The thought is that the one who wants to write a test includes the framework and writes the TEST_CASEs' implementations and compiles. Did I explain myself correctly? – pingul Apr 29 '14 at 13:11
  • You should not have function definitions in a header file.(period) – this Apr 29 '14 at 13:27
  • Don't re-invent the C language or attempt to replace it with your own macro language. When writing unit tests, the code should be as simple as possible. You should not aim towards creating problems in need of solutions, but rather aim towards solving problems that arise when you test the code. You shouldn't mix your test code with the production code. Solution to everything: make plain C function calls from a test file located in a separate test project. – Lundin Apr 29 '14 at 14:09
  • @Lundin I am not sure I understand your point; the question was to make the creation of tests easier. Do you mind clarifying what you mean? – pingul Apr 29 '14 at 19:35
  • @pingul What you have posted is not the C language syntax. You can expect C programmers to know C. You can not expect them to know your home-brewed macro language. The person writing the tests needs to know enough C to be able to easily write a function or call it. If doing so is a monumental, time-consuming task for them, they are incompetent and should not be writing the tests. – Lundin Apr 30 '14 at 06:39

3 Answers3

1

You can't really do it exactly the way you want, but if you're ok with the user having to do a bit more work, like eg. :

TEST_CASE(TestName1) {
    return 42;
}

TEST_CASE(TestName2) {
    return 0;
}

ALL_TEST_CASES {
    REGISTER_TEST_CASE(TestName1);
    REGISTER_TEST_CASE(TestName2);
}

then your framework could have code like this (using function pointers to register the test cases) :

typedef struct TestCase {
    const char* name;
    int (*fptr)();
} TestCase;

TestCase testCases[10];
size_t testCnt = 0;

#define ALL_TEST_CASES void registerTestCases()
#define TEST_CASE(NAME) int NAME()
#define REGISTER_TEST_CASE(NAME) do { testCases[testCnt].name = #NAME; testCases[testCnt++].fptr = &NAME; } while (0)

and the main :

int main(void) {
    registerTestCases();

    size_t i;
    for (i = 0; i < testCnt; ++i) {
        fprintf(stdout, "Test %s : %d\n", testCases[i].name, testCases[i].fptr());
    }

    return 0;
}

Obviously this needs to be made more robust etc. - it should just serve as a proof of concept.

Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
  • I think I was a little unclear with my question. I'll add an edit to explain myself better. – pingul Apr 29 '14 at 13:12
  • @pingul : adapted my answer according to your changes to the question. – Sander De Dycker Apr 29 '14 at 13:56
  • Great answer! I am just beginning to learn about macros and stuff; is there a reason for why you have to do `do {testCases...}` in the `REGISTER_TEST_CASE(NAME)` macro? – pingul Apr 29 '14 at 19:28
  • 1
    @pingul : it's a [nice "trick"](http://stackoverflow.com/questions/257418/do-while-0-what-is-it-good-for) for having multiple statements as part of a macro that work will with surrounding code. – Sander De Dycker Apr 29 '14 at 20:06
1

You could redefine the TEST_CASE macro depending on situation, so it does different things in different cases.

Something like

#define TEST_CASE(name)  int Test##name(void)

TEST_CASE(Function1)
{
    // Test Function1
}

TEST_CASE(Function2)
{
    // Test Function2
}

#undef TEST_CASE
#define TEST_CASE(name) { #name, Test##name }

static const struct
{
    char *name;
    int (*func)(void);
} tests[] = {
    TEST_CASE(Function1),
    TEST_CASE(Function2),
    { NULL, NULL }
};

int DoTests(void)
{
    for (int test = 0; tests[test].name != NULL; ++test)
    {
        printf("Running test #%d (%s)...", test + 1, tests[test].name);
        fflush(stdout);

        if (tests[test].func != NULL)
        {
            if (tests[test].func() == 0)
            {
                printf("Failed\n");
                return 0;
            }
        }

        printf("Ok\n");
    }

    return 1;
}

Then in your main function just call DoTests() and all tests should be called

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

There's no support in C to do what I think you want to do. That being said, if you want to use some unportable tricks you can take a peek at this question and answer: How can I implement a dynamic dispatch table in C

Community
  • 1
  • 1
Art
  • 19,807
  • 1
  • 34
  • 60