views:

403

answers:

11

I am writing unit tests for some of my code and have run into a case where I have an object with a small exposed interface but complex internal structures as each exposed method runs through a large number of internal functions including dependancies on the object's state. This makes the methods on the external interface quite difficult to unit test.

My initial question is should I be aiming to unit test these internal functions as well, as they are simpler and hence easier to write tests for? My gut feeling says yes, which leads to the follow-up question of if so, how would I go about doing this in C++?

The options I've come up with are to change these internal functions from private to protected and use either a friend class or inheritence to access these internal functions. Is this the best/only method of doing this will preserving some of the semantics of keeping the internal methods hidden?

+10  A: 

Short answer: yes.

As to how, I caught a passing reference on SO a few days ago:

#define private public

in the unit testing code evaluated before the relevant headers are read...
Likewise for protected.

Very cool idea.


Slightly longer answer: Test if the code is not obviously correct. Which means essentially any code that does something non-trivial.


On consideration, I am wondering about this. You won't be able to link against the same object file that you use in the production build. Now, unit testing is a bit of an artificial environment, so perhaps this is not a deal-breaker. Can anyone shed some light on the pros and cons of this trick?

dmckee
You're right, testing *is* a special case, so I guess I can't really complain about "#define private public" in this case. Still sorta *feels* unhealthy though! :)
j_random_hacker
It seems like a neat idea. It'd work in individual developer environments, but I'm not sure what kind of impact it would have if/when the unit tests are integrated into the production build process.
dlanod
*Theoretically*, two classes that contain the same members but with different access specifiers may get different memory layouts, as the one with private members is not "POD." So "#define private public" may break the ABI, leading to crashes -- although I doubt it will with any existing compiler.
j_random_hacker
Defining the unit test class as a friend class (as per Andrew Stein's comment) gave me the access provided by this #define while keeping the interface consistent between unit tests and production.
dlanod
For myself I usually make stuff protected rather than private, so this works just as well.
dmckee
A: 

You could always use a compile switch around the private: like

#if defined(UNIT_TEST)

Or with code coverage tools verify that your unit testing of your public functions fully exercise the private ones.

apekarske
A: 

Yes you should. Its called white box testing, this means that you have to know alot about the internals of the program to test it properly.

I would create public 'stubs' that call the private functions for testing. #ifdef the stubs so that you can compile them out when the testing is complete.

gbrandt
+2  A: 

I would say to use a code coverage tool to check if these function are already tested somehow.

Theoretically if your public API is passing all the tests then the private functions are working fine, as long as every possible scenario is covered. That's the main problem, I think.

I know there is tools for that working with C/C++. CoverageMeter is one of them.

Augusto Radtke
You raise a good point. If the public interface passes all tests, it should imply that the internal functions I'm using do so as well (assuming my selecting of test cases is up to scratch). However it does seem like the extra resolution would be useful in focusing on where a test failure occured.
dlanod
Keep in mind that coverage doesn't guarantee correctness.
Ates Goral
Especially in the presence of templates - coverage is 100% necessary, but 0% sufficient.
Tom
+2  A: 

Unless you're making a general purpose library you should try and limit what you have built to what you will be using. Extend as you need it. As such, you should have full code coverage, and you should test it all.

Perhaps your code is a bit smelly? Is it time to refactor? If you have a large class doing lots of things internally, perhaps it should be broken into several smaller classes with interfaces you could test separately.

Greg Domjan
+5  A: 

My feeling personally is that if testing the public interface is not sufficient to adequately test the private methods, you probably need to decompose the class further. My reasoning is: private methods should be doing only enough to support all use-cases of the public interface.

But my experience with unit testing is (unfortunately) slim; if someone can provide a compelling example where a large chunk of private code cannot be separated out, I'm certainly willing to reconsider!

j_random_hacker
+13  A: 

If your object is performing highly complex operations that are extremely hard to test through the limited public interfaces, an option is to factor out some of that complex logic into utility classes that encapsulate specific tasks. You can then unit test those classes individually. It's always a good idea to organize your code into easily digestible chunks.

Ates Goral
+5  A: 

There are several possible approaches. presuming your class is X:

  1. Only use the public interface of X. You will have extensive setup problems and may need a coverage tool to make sure that your code is covered, but there are no special tricks involved.
  2. Use the "#define private public" or similar trick to link against a version of X.o that is exposed to everyone.
  3. Add a public "static X::unitTest()" method. This means that your code will ship linked to your testing framework. (However, one company I worked with used this for remote diagnostics of the software.)
  4. Add "class TestX" as a friend of X. TestX is not shipped in you production dll/exe. It is only defined in your test program, but it has access to X's internals.
  5. Others...
Andrew Stein
I like option 4. It's standard-conforming, and limits the violation of encapsulation to a single class. And if you encounter a problem in the field, you can compile a different version of TestX and use that to peek inside the released code.
j_random_hacker
+3  A: 

My opinion is no, generally they should not be tested directly.

Unit tests are white box, from a higher perspective of the system, but they should be black box from the perspective of the tested class interface (its public methods and their expected behavior).

So for example, a string class (that wouldn't need legacy char* support):

  • you should verify that its length() method is working correcly.
  • you should not have to verify that it puts the '\0' char at the end of its internal buffer. This is an implementation detail.

This allows you to refactor the implementation almost without touching the tests later on
This helps you reduce coupling by enforcing class responsibilities
This makes your tests easier to maintain

The exception is for quite complex helper methods you would like to verify more thoroughly.
But then, this may be a hint that this piece of code should be "official" by making it public static or extracted in its own class with its public methods.

total
Totally agree. Tests need to be maintainable too!
j_random_hacker
+2  A: 

I've always thought this would tend to fall into place if you use test driven development. There are two ways of approaching the development, either you start with your public interface and write a new test before each addition to the complex private methods or you start off working on the complex stuff as public and then refactor the code to make the methods private and the tests you've already written to use the new public interface. Either way you should get full coverage.

Of course I've never managed to write a whole app (or even class) in a strict tdd way and the refactoring of the complex stuff into utility classes is the way to go if possible.

Patrick
A: 

You might find it productive, to write a test program. In the test program, create a class which uses the class to be tested as a base.

Add methods to your new class, to test the functions that aren't visible at the public interface. Have your simply test program, call these methods to validate the functions you are concerned about.

EvilTeach