At first we did not get to use the other Ruby tools from Atomic Object apart Unity unit test framework, but implemented similar, but simpler, in Python. That was pretty much because we did not "get" the tools, being electrical engineers and all.
Then we realized that we do not have the time and energy, nor the skills, to develop these tools seriously, but should use existing tools out of the box as much as possible. We started using Atomic Object's Ruby tools. So now the same project is using its third tool set for unit testing.
Rake for building the tests
I can't do magic with Rake - yet. I get the build script to work, but it is structured pretty much like scripting languages I'm used to, and most of the things are more complex than they should. I however have gotten a sight of its power and it is definatelly my choise of build scripting at the moment. The online manual has simple examples of using Rake to build C projects. It makes a good starting point. Advanced examples can be found in Atomic Object's tool distribution. You can check some more in-depth analysis on Rake by Martin Fowler here.
Unity unit test framework
Just above in complexity of pure macro implementation, or standard assert's, is Unity. It's written in C, with few helper macros. It's intelligence is in it's simplicity. There is no support for suites or anything like that. What to build and run can be grouped in build script.
Here's what you need for a unit test in Unity:
static void testAfterAdding4NodesTheSizeOfListShouldBe4(void)
{
int a;
struct list list;
list_Init (&list);
list_AddHead (&list, &a);
list_AddHead (&list, &a);
list_AddHead (&list, &a);
list_AddHead (&list, &a);
TEST_ASSERT_EQUAL(4, list_GetSize (&list));
}
int main(void)
{
Unity.TestFile = __FILE__;
UnityBegin();
// RUN_TEST calls runTest
RUN_TEST(testAfterAdding4NodesTheSizeOfListShouldBe4);
UnityEnd();
return (int) 0;
}
If you don't use mocking techniques you can just define RUN_TEST macro to call your setUp, test, and tearDown functions. That's it. In full approach it will call your mock module setup and verify functions also.
Cmock - automatic mock file generator
The other day a colleague interested in TDD'ing C said to me; "the biggest problem to me is that in C we allways need some real friends for a module under test. We do not have real mechanism to support interfaces." Well yes, and no. We concider interface to be the .h file, or in more advanced cases a struct of function pointers. After that we get to automating stuff. Cmock is a tool that generates mock functionality based on .h file. It goes through all .h files in your source directory and generates a corresponding mock.c files. These mocks can be linked together with the module under test and can be teached to expect calls and to return values. Here's an simple example of a function definition and resulting teaching function:
uint8_t Protection_filter_getState( uint8_t me_index);
void Protection_filter_getState_ExpectAndReturn( uint8_t me_index, uint8_t toReturn);
Argent automatic test code generator
It's a pain to add mock init and verify functions, as it is to add a call to test function to main function, or just to change a name of test in two different places. That's the pain - Argent is the relief. It uses a simple markup scheme, and interprets your test code in order to automatically insert the calls to appropriate test functions, and mock module initialization and verifications.
//[[$argent require 'cmock_injector.rb'; inject_mocks("lists");$]]
// Calls to mock objects will be inserted here accoring the included mock headers.
//[[$end$]]
//[[$argent require 'generate_unity.rb'; generate_unity();$]]
// Calls to test functions (identified by test prefix) will be inserted here.
//[[$end$]]
Mock dependency check
It's also a pain to add mock dependencies to linker command. Using the same interpreter as argent the mock dependencies can be checked at build time automatically. So all that is needed to take a mock object into build is to have it's header included in the test file.
So that's how far we are now. Now we need some experience to get our routines right.
I quess this is a good example of having courage to improve when we see an opportunity, and strenght to admit being dead wrong - over and over again. This is truly a learning experience.