While working in C for an embedded for PIC microprocessor system and at the same time working on a Java & Laszlo project using decent mock libraries, I found myself wishing for auto-mocking for C. We were already writing our embedded code following a Model-View-Presenter design pattern for Test Driven Development. If we were to add a mocking mechanism, we needed something small and simple that would fit into a system with 256 bytes of RAM and 32K of ROM. Using Ruby, combined with the Rake build scripts we were already using, we implemented an initial version of a C mock library during a couple slow days at a conference. Using Ruby and rake external to the target system, we generated C code and headers with functionality equivalent to that available in object oriented languages with reflection. The generated C code adds little to no overhead to the actual production system.
We already had our code organized into ‘classes’ of different types of *Models, *Conductors (Presenters) and *Hardware (Views). The system we came up with involves parsing the header for each class in the system, generating a matching mock .c and .h file which implemented the functions in the header, and also adding auto-generated functions for handling the expect and return calls. The build system automatically links in mocks for all classes. In a specific test case, the entire system is being mocked out except for the one class under test. We call this collection of tools CMock. We’ll be releasing it open source soon.
The simplest way to understand all this is with some examples. We have several *Calculator classes that are used as wrappers around our Models to isolate math from the storage and gathering of data in the models. Doing this means that all the numbers used in a calculation in a Calculator class can be injected via mocks during the test. The Calculator itself has no state but gets state information from the Models, and we can control the mocked models to give us specific values. Another benefit of the Calculators is that they wrap several calls to different states in the model into one method call in other parts of the system; one method which can be simply mocked to return a test value in other tests.
The GetCurrent() method in the CurrentCalculator class returns battery charge or consumption current in quarter amps. This calculation involves calls to get state from the ModelConfig class. Other Calculator classes convert input voltage from a current sensor into the actual current the battery is consuming or being charged with. The code also handles the added requirement of reporting either averaged current or instantaneous current.
Here’s the test for this method. Note that averaged current is being calculated here because we’ve mocked out the ModelConfig class to return the Average display mode state. Instantaneous calculation is tested in another function with the mock returning a different mode.
The calls with _Expect, _Return and _ExpectAndReturn are the auto generated methods created by mock tools. They correspond to methods in those classes (by adding _Expect, etc. to the name of the method).
You can see that calculating current involves several steps, but the GetCurrent method delegates almost all its functionality (except for combining the collected values into one relatively simple formula—as seen in the comment). The use of mocks has made it easier not only to assert that the function is getting the right data at the right time and in the right order but has also made writing the method itself easier. Each method we’ve written generally only has to deal with the one real piece of functionality it is adding while getting everything else from delegates.
GetCurrent() is one of the longer methods in our system as can be seen by the number of expect calls. (Most methods involve one or two calls to delegates and then a line of functionality.)
Looking at one of the specific methods being used by GetCurrent() we can show what the mock tools do.
This line is from ModelConfig.h. ModelConfig is basically a state holder for configuration of the circuit board. DisplayMode is an enumeration of Average or Instantaneous.
This line is from MockModelConfig.h and is autogenerated. When this method is called, the mock library stores the parameter in a list, and the generated version of GetCurrentDisplayMode() returns values from that list in the order in which the _Return() method is called.
When the Verify method is called for each mock class it will check all the expected calls and also report any unexpected calls. Parameters passed to mocked methods are also verified.
To save the trouble of initializing and verifying each mock, we use Argent to automatically do all the initialization and verification for use. This is done in combination with our rake build scripts in order to know what every other class in the system is.
//[[$argent require ‘generate_unity.rb’; generate_supermock(“CurrentCalculator”);$]]
This is the line of code that is processed by Argent for TestCurrentCalculator.c. It calls a ruby function and places the output into the source file. This particular function knows the name of every class in the project. Given the parameter which is the name of the class under test it creates three functions for initializing, verifying, and destroying the mocks for everything else.
We also use Argent with our custom unit test library (Unity—I will write about it another day) to automatically insert calls for every test function in the file. So, writing behavior-based unit tests for C is as simple as just writing the test function with the mock calls we want with no setup needed at all.
In my experience, with this set of tools, we’ve made C one of the simplest languages for doing test driven development. RMock for Java comes close, but in my opinion our approach here is a great advancement over even anything I’ve seen for C+. (Although, something like what we did here for C could easily be done for C+ as well).
CMock does not work (even does not generate compilable code) if the function prototype has pointers, structures or any complex data structures.
Great job… so excited to be able to use TDD and agile methods for
my current embedded project. But the enthusiasm has been tempered since spending a week fixing some problems in the CMock framework.
it doesn’t seem to handle functions with the signatures like
unsigned int foo(unsigned int param1, unsigned int param2);
very well. Has someone tried this and it worked?
So sorry about the trouble you’re having but also so thrilled that you’re using these tools in actual TDD embedded work. The demo version we posted was an early rev of the tools. I’m not entirely sure what the problem you’re having might be. In just a couple weeks, in relation to our participation in GLSEC 2007 (http://glsec.org), we’re releasing far more fully baked versions of the tools. We’ve learned a lot using them in a large project and have incrementally improved them as we’ve gone.
[…] Object's Blog On Software Design & Development « CMock – (ruby based) mock tools for C Presenter First video » Double Entry Programming By Bill Bereza | Published: December 29, […]
Comments are closed.