Satish BysanyArchiveAbout

Unit testing and mocking with cmocka

cmocka is a C library for unit testing and mocking. It provides a decent alternative to C++ frameworks like Google Test/Mock or CXXUnit. This post is a quickstart guide.

Cmocka is available in Debian/Ubuntu repos. The version I used was 1.0.1.

sudo apt-get install libcmocka-dev

Testing via assertions

The cmocka API is detailed. Test cases are collected in an array of CMUnitTest. Each testcase is simply any function whose signature matches

void func_name(void** state);

As in the example below, you need to include cmocka.h and setjmp.h.

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

static void test(void** state)
{
    assert_int_equal(3, 3);
    assert_int_not_equal(4, 4);
}

int main(void)
{
    const struct CMUnitTest tests[] =
    {
        cmocka_unit_test(test),
    };

    return cmocka_run_group_tests(tests, NULL, NULL);
}

Compile and run:

gcc test1.c -lcmocka && ./a.out

[==========] Running 1 test(s).
[ RUN      ] test
[  ERROR   ] --- 0x4 == 0x4
[   LINE   ] --- test1.c:9: error: Failure!
[  FAILED  ] test
[==========] 1 test(s) run.
[  PASSED  ] 0 test(s).
[  FAILED  ] 1 test(s), listed below:
[  FAILED  ] test

 1 FAILED TEST(S)

As expected, the failure was highlighted with the ‘expected’ and ‘actual’ values along with line numbers. There are assert macros to compare primitive data types, as well as arrays and strings.

Mocking via __wrap

Assume that in production code we need to check if money was successfully deposited in a bank:

int production_code(int money, const char* bank)
{
  int ret = EXIT_FAILURE;

  if (bank) {
    ret = deposit(money, bank);
  }

  return ret;
}

Let’s assume that the function signature is declared in “deposit.h” as:

// The real function "deposit()" must satisfy these requirements
// 1. Accept deposit only if account is valid
// 2. Only accept deposits greater than 100
// 3. We can deposit in any bank other than Nordea Bank
int deposit(int money, const char* bank);

We want to test our production_code() for different values of inputs. However, the deposit() function is in third-party library, for which we don’t have the real code, only the above interface is available. So, we’ve to mock it. Mock functions in cmocka always start with __wrap because the GNU linker (ld) has this lesser-known flag:

 --wrap=symbol
       Use a wrapper function for symbol.
       Any undefined reference to symbol will be resolved to "__wrap_symbol". ...

Hence, if we define a function called “__wrap_deposit” then the linker will call this wrapped function instead of the original, real deposit() function defined in the external library.

We can emulate the deposit function using mock_types and check_expecteds for the behaviour documented in the interface:

int __wrap_deposit(int money, const char* bank)
{
  // Always ensure input parameters are what're expected
  check_expected(money);
  check_expected_ptr(bank);

  // We can define as many mock_types as we want.
  bool is_valid_account = mock_type(bool);
  if (!is_valid_account) { return EXIT_FAILURE; }

  int minimum_money = mock_type(int);
  if (minimum_money <= 100) { return EXIT_FAILURE; }

  char* bank_name = mock_ptr_type(char*);
  if (strcmp(bank_name, "Nordea") == 0) { return EXIT_FAILURE; }

  return EXIT_SUCCESS;
}

In our test code, we can pre-program the values for mock_types and ensure that the deposit function is called with right parameters:

static void test_successful_deposit(void** state)
{
  (void)state;  // unused variable
  int deposit_money = 200;
  const char* deposit_bank = "Citibank";

  // These expects must match the order of declaration of "check_expected"
  // in __wrap_deposit()
  expect_value(__wrap_deposit, money, deposit_money);
  expect_string(__wrap_deposit, bank, deposit_bank);

  // These will_return's must match the order of declaration of "mock_type"s
  // in __wrap_deposit()
  will_return(__wrap_deposit, true);  // assume account is valid
  will_return(__wrap_deposit, deposit_money);
  will_return(__wrap_deposit, deposit_bank);

  // Test the production code
  int ret = production_code(deposit_money, deposit_bank);

  assert_int_equal(ret, EXIT_SUCCESS);
}

int main(void)
{
  const struct CMUnitTest tests[] = {
    cmocka_unit_test(test_successful_deposit)  };

  return cmocka_run_group_tests(tests, NULL, NULL);
}

Now comes the linker magic:

gcc test3.c -I. -Wl,--wrap=deposit -lcmocka && ./a.out
[==========] Running 1 test(s).
[ RUN      ] test_successful_deposit
[      OK  ] test_successful_deposit
[==========] 1 test(s) run.
[  PASSED  ] 1 test(s).

A more complete example is in this snippet.

Conclusions

  1. There are good assert macros to enable unit testing
  2. Wrapping every mocked function on the CLI with -Wl,--wrap gets tedious if there are hundreds of functions in the production code that you want to mock.
  3. If any of the will_return calls fail, there is no indication from cmocka’s output. GMock, for example, will notify the user of “uninteresting function call” or “called more times than expected” but cmocka will silently fail.

References

  1. LWN article with a good background motivation
  2. Chef and Waiter example
  3. Parameter checking API
  4. will-return API