Using CMock when C is Oversensitive

CMock is an extremely useful tool that can make testing C programs nearly painless. Employing CMock is usually straightforward, but here are a few common sticking points and ways to work around them:

Calling Functions in the Same Module

How do you test a function that calls another function in the same module? Since the function being called is in the same module as the function under test, CMock can’t mock it.

The best way to handle this is to move the functions into separate modules. In some cases if the function not going to be called outside the module (static) and is simple enough, then it is sometimes easier to keep it in the same module but test it as if the function is really just a part of the functions that calls it.

static void Helper(uint32_t a)
{
  DoSomething(a);
  DoSomethingElse(2*a);
}
void MyFunction()
{
  …
  Helper(x);
}

void test_MyFunction()
{
  …
  DoSomething_Expect(x);
  DoSomethingElse_Expect(2*x);
}

However, if your helper functions are not declared static or if they are even moderately complicated, you should move them into separate modules.

Wait, doesn’t that mean that if I have a chain of functions in a file where A calls B calls C calls D, that I might have to break them out into 4 files to mock them properly?

Yes. It’s kinda annoying, and goes against common practice, but results in much better tests.

Inlines and Macros

Mocks for  macros or inlined functions have to be handled specially. Check out the solution here.

Mocking Functions that Take Values Passed by Reference

The default behavior when dealing with pointers in an “Expect” is to compare the memory pointed to by the two pointers, rather than compare the pointers themselves. This is generally what works best, but there are a few corner cases:

  • You really do want to compare the values of the pointers:

   There are two ways of handling this:

      • Use a custom type helper (the preferred method). This is covered here.
      • Use a callback. (we’ll get to this in a second)

This is quite common and can cause subtle and intermittent problems. CMock does a memory compare to compare the value pointed to by the two pointers. If you are not careful how you initialize the two structures CMock might intermittently fail the comparison on the padding bytes. To solve this problem, you either need to use a custom type helper (as mentioned earlier), or you have to make sure you initialize the structures in such a way that ensures the padding bytes are set to known values. memset(&x, 0x00, sizeof(x) does this nicely.

  • You want the mock to modify a value that is passed by reference.
    • Use a callback (see below)

When CMock generates mocks for a module it creates YourFunction_StubAndCallback functions for each of your mocked functions.

So, say we are mocking this function:

bool MyFunction(uint_32_t * x)

and we want to change the value pointed to by x in the mock, we can do that like so:

We make a callback function that has the same signature as MyFunction, but takes an extra int num_calls parameter. Every time our mock is called this callback will be called with num_calls set to the number of times the function has been called so far (starting at 0).

bool MyFunction_Callback(uint32_t * x, int num_calls)
{
  switch (num_calls)
  {
    case 0: // first time function is called
      return false;
    case 1: // second
      *x ++;
      return false;
    case 2:
      return true;
    default:
      TEST_FAIL_MESSAGE("MyFunction was called to many times!");
  }
  return false;
}
//the function under test:
void SomeFunctionThatCallsMyFunction()
{
  int j=5;
  ...
  while (!MyFunction(&j))
  {
    ...
    SomeOtherFunction(j);
  }
}
void test_TestSomeFunctionThatCallsMyFunction()
{
  int i=5;
  …
  // This tells CMock to use our callback.
  MyFunction_StubWithCallback(MyFunction_Callback);
  // You still need ExpectAndReturn's (this is configurable)
  // The return value from the _ExpectAndReturn is ignored
  //  the return value from the callback is used instead.

  // The first call doesn't change 'i'
  MyFunction_Next_ExpectAndReturn(&i, false);
  SomeOtherFunction_Expect(5);
  // The second call does!
  MyFunction_Next_ExpectAndReturn(&i, false);
  SomeOtherFunction_Expect(6);
  MyFunction_Next_ExpectAndReturn(&i, true);  

  // Call function under test
  SomeFunctionThatCallsMyFunction();

}

Callbacks are kinda clunky, but give a lot of flexibility.
 

Conversation
  • Anshul says:

    Hi Job,
    Thank you for basics
    Please tell me how to handle typedefs in CMock
    I have typedef to my struct (i.e typedef struct custom_axz_struct handle)

    I am getting error “error: invalid application of ‘sizeof’ to incomplete type ‘handle’”

    Regards,
    Anshul

  • Comments are closed.