Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Fixtures in cmocka

I'm working on a project that's using the cmocka framework. The cmocka homepage states

Test fixtures are setup and teardown functions that can be shared across multiple test cases to provide common functions that prepare the test environment and destroy it afterwards.

However, none of the docs I've read explain how the fixture system works.

If I'm running my tests with code that looks like this

int main(void) {
  const struct CMUnitTest license_tests[] = {
      cmocka_unit_test(test_setup),                        
      cmocka_unit_test(test_null_app),      
      cmocka_unit_test(test_null_name),        
  };

  return cmocka_run_group_tests(license_tests, NULL, NULL);
}

How/where can I instruct cmocka to run setup/tear down fixtures and what features (if any) does cmocka have for letting me access things created in said fixtures?

like image 938
Alan Storm Avatar asked Nov 10 '17 22:11

Alan Storm


2 Answers

Here is a template unit testing file that you can reuse in your project. It tackles all your requirements

#include <stdio.h>
#include <cmocka.h>

#include "mylib.h"

// Include here all your mocked functions, see below
#include "testmocks.c"

typedef struct {
  mylibobj_t* mylibobj;
} teststate_t;

/**
 * This is run once before all group tests
 */
static int groupSetup (void** state) {
  teststate_t* teststate = calloc(1, sizeof(teststate_t));    
  *state = teststate;
  return 0;
}

/**
 * This is run once after all group tests
 */
static int groupTeardown (void** state) {
  teststate_t* teststate = *state;
  free(teststate);
  return 0;
}

/**
 * This is run once before one given test
 */
static int testSetup (void** state) {
  teststate_t* teststate = *state;
  //Maybe instantiate mylibobj?
  return 0;
}

/**
 * This is run once after one given test
 */
static int testTeardown (void** state) {
  return 0;
}

/**
 * This test will success with these options
 */
void mylib_setTime_s0 (void** state) {
  teststate_t* teststate = *state;
  // Do your testing stuff
}

/**
 * This test will fail with these options
 */
void mylib_setTime_f0 (void** state) {
  teststate_t* teststate = *state;
  // Do your testing stuff
}

int main (void) {
  const struct CMUnitTest tests[] = {
    cmocka_unit_test_setup_teardown(mylib_setTime_s0, testSetup, testTeardown),
    cmocka_unit_test_setup_teardown(mylib_setTime_f0, testSetup, testTeardown),
  };
  return cmocka_run_group_tests(tests, groupSetup, groupTeardown);
}

The testmocks.c exists just for code organization, and may hold 0 to N pairs of mocked functions as

#define MOCKBYPASS  -7337

mylib_status_t __real_inner_function (char* id);
mylib_status_t __wrap_inner_function (char* id) {
  int mock = mock();
  if(mock == MOCKBYPASS)
    return __real_inner_function(id);
  else
    return mock;
}

...

Remember that gcc compilation tricks are a must for these mocks to work correctly

like image 142
debuti Avatar answered Oct 19 '22 06:10

debuti


Those two NULL's you are passing to cmocka_run_group_tests function should, be group_setup and group_teardown functions, both type CMFixtureFunction - if You want to utilize

setup and teardown functions that can be shared across multiple test cases

in cmocka called test fixtures. As documentation for this function states.

Then shared (void** state) is accessible in each test. It can be used as in example below:

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cmocka.h>

#define GREETINGS "one string to rule them all, One string to find them, "\
                  "one string to bring them all and in the darkness bind them"
#define PRECIOUSS_T1 "Three strings for the Elven-kings under the sky,"
#define PRECIOUSS_T2 "even for the Dwarf-lords in halls of stone,"
#define PRECIOUSS_T3 "Nine for Mortal Men, doomed to die,"
#define PRECIOUSS_T4 "One for the Dark Lord on his dark throne"
#define PRECIOUSS_T5 "In the Land of Mordor where the Shadows lie."
#define PRECIOUSS_T6 "One string to rule them all, One Ring to find them,"
#define PRECIOUSS_T7 "One string to bring them all and in the darkness bind them."
#define PRECIOUSS_T8 "In the Land of Mordor where the Shadows lie."
#define OOPS "Not quite what I expected"
#define T(X) PRECIOUSS_T##X

#define FOO(X) case X: strncpy(lots->memory, T(X), sizeof(T(X))); break;
#define SPR_FOO(X) case X: assert_string_equal(T(X), lots->memory); break;

typedef struct {
          int    line;
          char * memory;
} lord_of_the_strings_t;

static int gr_setup(void **state) {
     /* this is run once before all group tests */
     lord_of_the_strings_t *book = malloc(sizeof(*book));
     if (book == NULL)
         return -1;
     book->memory = malloc(sizeof(GREETINGS));
     if (book->memory == NULL) {
         return -1;
     }

     strncpy(book->memory, GREETINGS, sizeof(GREETINGS));
     assert_string_equal(book->memory, GREETINGS);
     *state = book;
     return 0;
}

static int gr_teardown(void **state) {
     /* this is run once after all group tests */
     lord_of_the_strings_t *lots = *state;
     free(lots->memory);
     free(lots);
     return 0;
}

static int ve_teardown(void **state) {
     /* this is run before some single tests */
     lord_of_the_strings_t *lots = *state;
     lots->line = 42;
     return 0;
}

static int ve_setup(void **state) {
     /* this is run after some single tests */
     static int setup_counter = 0;
     lord_of_the_strings_t *lots = *state;
     lots->line = ++setup_counter;
     switch (setup_counter) {
        FOO(1)
        FOO(2)
        FOO(3)
        FOO(4)
        FOO(5)
        FOO(6)
        FOO(7)
        FOO(8)
        default:
           strncpy(lots->memory, OOPS, sizeof(OOPS));
     };
     return 0;
}

static void failing_test(void **state) {
     assert_false("Sorry");
}

static void line_aware_test(void **state) {
     lord_of_the_strings_t *lots = *state;
     printf("             (shared) between tests, line=%d memory=%s\n", lots->line, lots->memory);
}

static void passing_test(void **state) {
}
static void string_recite_test(void **state) {
     static int line_counter = 0;
     lord_of_the_strings_t *lots = *state;
     if (lots->line < 9)
        assert_true(line_counter+1 == lots->line);
     switch (++line_counter) {
        SPR_FOO(1)
        SPR_FOO(2)
        SPR_FOO(3)
        SPR_FOO(4)
        SPR_FOO(5)
        SPR_FOO(6)
        SPR_FOO(7)
        SPR_FOO(8)
        default:
            line_counter = 0;
     }
}


int main(void) {
        const struct CMUnitTest tests[] = {
            cmocka_unit_test(passing_test),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test(line_aware_test),
            cmocka_unit_test(line_aware_test),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test(line_aware_test),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test(line_aware_test),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test_setup_teardown(string_recite_test, ve_setup, ve_teardown),
            cmocka_unit_test(failing_test),
        };
        return cmocka_run_group_tests(tests, gr_setup, gr_teardown);
}

Which should yield output like

[==========] Running 21 test(s).
[ RUN      ] passing_test
[       OK ] passing_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] line_aware_test
             (shared) between tests, line=42 memory=Nine for Mortal Men, doomed to die,
[       OK ] line_aware_test
[ RUN      ] line_aware_test
             (shared) between tests, line=42 memory=Nine for Mortal Men, doomed to die,
[       OK ] line_aware_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] line_aware_test
             (shared) between tests, line=42 memory=One string to rule them all, One Ring to find them,
[       OK ] line_aware_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] string_recite_test
[       OK ] string_recite_test
[ RUN      ] string_recite_test
[  ERROR   ] --- "Three strings for the Elven-kings under the sky," != "Not quite what I expected"
[   LINE   ] --- aa.c:100: error: Failure!
[  FAILED  ] string_recite_test
[ RUN      ] string_recite_test
[  ERROR   ] --- "even for the Dwarf-lords in halls of stone," != "Not quite what I expected"
[   LINE   ] --- aa.c:101: error: Failure!
[  FAILED  ] string_recite_test
[ RUN      ] line_aware_test
             (shared) between tests, line=42 memory=Not quite what I expected
[       OK ] line_aware_test
[ RUN      ] string_recite_test
[  ERROR   ] --- "Nine for Mortal Men, doomed to die," != "Not quite what I expected"
[   LINE   ] --- aa.c:102: error: Failure!
[  FAILED  ] string_recite_test
[ RUN      ] string_recite_test
[  ERROR   ] --- "One for the Dark Lord on his dark throne" != "Not quite what I expected"
[   LINE   ] --- aa.c:103: error: Failure!
[  FAILED  ] string_recite_test
[ RUN      ] string_recite_test
[  ERROR   ] --- "In the Land of Mordor where the Shadows lie." != "Not quite what I expected"
[   LINE   ] --- aa.c:104: error: Failure!
[  FAILED  ] string_recite_test
[ RUN      ] string_recite_test
[  ERROR   ] --- "One string to rule them all, One Ring to find them," != "Not quite what I expected"
[   LINE   ] --- aa.c:105: error: Failure!
[  FAILED  ] string_recite_test
[ RUN      ] failing_test
[  ERROR   ] --- "Sorry"
[   LINE   ] --- aa.c:79: error: Failure!
[  FAILED  ] failing_test
[==========] 21 test(s) run.
[  PASSED  ] 14 test(s).
[  FAILED  ] 7 test(s), listed below:
[  FAILED  ] string_recite_test
[  FAILED  ] string_recite_test
[  FAILED  ] string_recite_test
[  FAILED  ] string_recite_test
[  FAILED  ] string_recite_test
[  FAILED  ] string_recite_test
[  FAILED  ] failing_test

 7 FAILED TEST(S)
like image 24
JustMe Avatar answered Oct 19 '22 06:10

JustMe