search the lab

Embedded Testing with Unity & CMock (by Mark)

Embedded Testing with Unity & CMock

James Grenning's book (features Unity)

Test Driven Development for Embedded C

Topics

Entries in Pointers (1)

Wednesday
Feb162011

CMock, Pointers, and Structs

CMock will likely directly support structures and pointers in the near future. But, we've been using the current form of CMock for a while now, and have learned a number of tricks that will help you to write good clean code which is also happily compatible with CMock. It might require a couple of minor philosophy tweaks at this point, but here are some tricks we have found useful. For example, you may have a local variable / struct / etc that you need to pass by pointer, like here:

void SuperGreatFunction(void)
{
    MY_STRUCT_T MyStruct;

    FunctionToBeMocked(&MyStruct);
}

This is, of course, slightly painful because CMock currently wants to test that pointer and it's something local. First of all, we avoid situations like this (more on this in a moment), but when it IS required, our approach is something like this:

#ifdef TEST
    MY_STRUCT_T MyStruct;
#endif

void SuperGreatFunction(void) 
{ 
#ifndef TEST 
    MY_STRUCT_T MyStruct; 
#endif

    FunctionToBeMocked(&MyStruct); 
}

The define TEST is automatically created whenever you are running Unity tests, so it can be used to make your local variables public for the purpose of your tests, but private outside your test. Similarly, functions which take a pointer to an object and modify it (a poor practice even when not doing testing, but made worse during a test situation because of the difficulty in modifying it at the appropriate place), we usually write the function to return a pointer to the modified object.

void AwesomeFunction(MY_STRUCT_T* MyStruct)

becomes

MY_STRUCT_T* AwesomeFunction(MY_STRUCT_T* MyStruct)

because you can now use AwesomeFunction_ExpectAndReturn(VersionYouExpectToGetPassedIn, VersionYouWantToReturnMostLikelyCreatedInYourTestOnly) With custom structures, in particular, we've found allocating and managing the structures within factory and warehouse modules to be a really good approach. Not only does it solve the limitation of structures with CMock, but, in fact, it promotes well designed code. We can segregate factory and warehouse modules from the code that consumes/uses the structures they manage - promoting better separation of use and allowing thorough but simple testing of instantiation and use of structures. We'll try to demonstrate with some example/pseudo code. Assume all but STATIC functions are declared in header files. We assume a STATIC macro exists that the preprocessor substitutes with 'static' in production builds and in test builds (thus allowing extern access to anything declared STATIC). It also helps avoid naming collisions when there's lots of pattern re-use. This isn't directly related to the topic at hand, but it's a style that is effective and We've used in this example.

////////////////////////////// 
// Foo.h 
////////////////////////////// 

typedef struct _Foo 
{ 
    bool_t IsOk; 
    uint32_t Value; 
} Foo; 

////////////////////////////// 
// Foo.c 
////////////////////////////// 

void Foo_SetIsOk(Foo* FooItem, bool_t Ok) 
{ 
    FooItem->IsOk = Ok; 
} 

bool_t Foo_GetIsOk(Foo* FooItem) 
{ 
    return FooItem->IsOk; 
} 
 
void Foo_SetValue(Foo* FooItem, uint32_t Value) 
{ 
    FooItem->Value = Value; 
} 
 
uint32_t Foo_GetValue(Foo* FooItem) 
{ 
    return FooItem->Value; 
} 

////////////////////////////// 
// FooFactory.c 
////////////////////////////// 
 
Foo* FooFactory_Manufacture(...params...) 
{ 
    // In real life you might want to use a malloc wrapper 
    // for mocking and testing. It can have certain advantages. 
    Foo* ptr = malloc(sizeof(Foo)); 
 
    ptr->IsOk = param1; 
    ptr->Value = param2; 
 
    return ptr; 
} 

////////////////////////////// 
// FooWarehouse.c 
////////////////////////////// 
 
// use STATIC macro to make private in production but expose in tests 
STATIC Foo* Warehouse[FOO_COUNT]; 
 
STATIC void FooWarehouse_Store(Foo* FooItem, FooEnum Id); 
 
void FooWarehouse_Fill(void) 
{ 
    FooWarehouse_Store(FooFactory_Manufacture(params, FOO_A)); 
    FooWarehouse_Store(FooFactory_Manufacture(params, FOO_B)); 
    FooWarehouse_Store(FooFactory_Manufacture(params, FOO_C)); 
} 
 
Foo* FooWarehouse_Retrieve(FooEnum Id) 
{ 
    return Warehouse[Id]; 
} 

// 
// Private Helpers 
// 
 
void FooWarehouse_Store(Foo* FooItem, FooEnum Id) 
{ 
    Warehouse[Id] = FooItem; 
} 

////////////////////////////// 
// FooUser.c 
////////////////////////////// 
 
void FooUser_DooSomething(FooEnum Id, uint32_t Bar) 
{ 
    Foo* foo; 

    foo = FooWarehouse_Retrieve(Id); 

    if (Bar == 1000) 
    { 
        Foo_SetValue(foo, Bar*2); 
    } 
    else 
   { 
        Foo_SetValue(foo, 0); 
    } 
} 

////////////////////////////// 
// TestFooUser.c 
////////////////////////////// 
 
void test_FooUser_DooSomething_ShouldClearFooValueIfParam1IsNot1000(void) 
{ 
    Foo TestFoo; 

    FooWarehouse_Retrieve_ExpectAndReturn(FOO_A, &TestFoo); 

    Foo_SetValue_Expect(&TestFoo, 0); 

    FooUser_DooSomething(FOO_A, 1001); 
}

In your code, you would gather together all your warehouse fill calls at initialization. From that point on, you can call the retrieve function on the warehouse you need with an enum to get a reference to Foo you need. Centralizing all your instantiation really helps structure your code and this method means you can mock all your instantiation/filling in tests. Wrapping getters/setters around your Foo struct's member values allows you to mock and test all manipulations of a Foo without directly accessing the member values in each case that a Foo is used. That is, you can limit direct structure manipulation to just the getter and setter functions and tests thereof. Mocking the getters, in particular, makes testing of conditional logic on the member values of the structure much easier elsewhere. Because CMock can handle pointer comparisons just fine, you can use the approach demonstrated to handle any custom structure anywhere.