A Pragmatic approach to Unittesting a class' Internals
When you drive your car, and you want to slow down, you hit the brake pedal. Under the pedal is a myriad of quite advanced systems, but as a driver all that technology is abstracted away from you by the car manufacturer so you can focus your attention on the traffic.
When I take my car to the yearly technical inspection, that changes. Now it's no longer sufficient if hitting the brake slows down the car, instead the test bank will check if all of the four wheels individually have the right amount of braking power, and that there is no substantial unbalance between all wheels.
It's an example of how using an object can be different for the typical end user (the car driver) and the unit tester (the car technician)
Let's translate the example above to a simple software example: a command-line parser. You enter a line of text, and it executes the command with the right arguments.
We need several helper-functions under the hood: to count the number of arguments, split the textline into separate tokens, recognize and validate the commands and their parameters. All of these helpers can be hidden from the end-user. Still, to decently test the parser, it would be great to test them in isolation, simply because isolating them, will drastically reduce the number of test-cases and makes each test simpler.
I have been hitting this dilemma too often, and did a lot of research to find a solution. Most unit-testing guru's simply declare that you only test the public interface, and that you don't care about the internals. Well, I disagree, and I hope that the examples above made it clear why. A more pragmatic approach is needed.
Here's my proposal :
领英推荐
* put your public: on top of the class (this is best practice anyway)
* put you private: at the bottom
* put a second private: section in the middle, with all internals you want hidden from the end user, but visible for the tests.
With a simple #ifndef unitTesting, this keyword will be outcommented when the compiler compiles for a unit test build.
class commandLineParser {
public:
void parseAndExecute(const char* lineOfText);
#ifndef unitTesting
private:
#endif
int numberOfArguments(const char* lineOfText);
void getToken(char* output, const char* input, int tokenIndex);
private:
...
};
Like or disagree, or maybe have a better solution ? Feel free to leave a comment.