Unit Test How? Private Data
This is part of an ongoing series of articles about using Unity to Unit Test C applications, often for Embedded Applications. If you're looking for other tips, be sure to check out the others here.
Today we're going to talk about private data. Private data often takes the form of module-scoped variables. They're meant to be shared amongst the functions in our module, but not to be seen by the outside world. The private data falls into two categories:
SOFT PRIVACY
If our privacy is soft, it means our variables are declared in the C file, but aren't really protected... so they are effectively global if anyone cares to extern
them. Of course, everyone knows they shouldn't extern them into their file and use them directly, but since we didn't actually protect them, we can't get too angry when this promise is broken.
HARD PRIVACY
We implement hard module privacy through the innocent-looking static
keyword. Once we declare that variable as static, it's name doesn't leave the current module and can't be accessed without jumping through serious hoops. This is a good practice to make sure that those accessing our modules are accessing just the data and methods that we want them to have access to. Everything else should be defined as static.
CAN WE TEST IT?
Well, clearly we CAN test things that are soft private. All we need to do is use extern in our test. Boom. We have access to anything we want. As we'll discuss in a moment, we CAN also test hard private data and functions.
Before we discuss that, though, we should talk about a bigger question: SHOULD we test it? Needing access to internal data is often an indicator that our module is not built as well as it could be. It's a [code smell]. It doesn't necessarily mean we've screwed up... but it's an indicator we might have.
Before using a skeleton key to give ourselves access to private data, we should see if there is a cleaner way to test the module. Can we treat it more like a black box? Can we refactor the module itself to be cleaner?
Still convinced we need to get at the data? It DOES happen that this is the best way. So let's do it.
FABRIC SOFTENER
Our skeleton key in this case is fabric softener! We want to turn hard private data into soft private data. If we control the source and the test, this is actually very simple.
#ifndef TEST
#define STATIC static
#else
#define STATIC
#endif
Instead of declaring our module-scoped variables with static
, we instead declare them as STATIC
. Our scoping rules are still applied when compiled for release, but the hard privacy is dropped when compiled for a test. We can then reach in using extern
and read/write the data as needed.
ACCESS GRANTED
That's all there is to it. One particularly useful application of this is to use private data to build up particularly complex modules in a test driven fashion (a state machine, for example). Once built, we write tests that don't use the private data to test the overall system behavior. We can now drop the temporary tests.
In any case, I hope this is useful for you!
Happy Coding!