Contents
Don’t get me wrong. Unit tests are your friends when developing a class. But they should not be that class’ `friend`. Here’s why.
Tight coupling
Declaring unit tests `friend` of the class under test incurs the tightest coupling there is in C++. `friend` is even a tighter coupling than inheritance – `friend`s can access private members, while derived classes can only access protected members.
Giving unit tests access rights to private members means they become dependent on way more than just the public interface, and any internal maintenance of the class effects the unit tests as well.
Production code invasion
Unit tests are there to support and, in case of Test Driven Development, guide the development of the production code. Unit tests often influence design decisions, usually improving the design by making it more modular and more maintainable.
However, unit test code should not invade the production code. If you have to pepper your production code with names of classes that are only relevant for the tests then those tests start to become a burden.
Causes for wanting a friend
When you feel the need to make a unit test friend of the class under test, this might be a sign that something else is wrong with your code or your tests:
Not testing the right thing
You may say you need private access if you want to test the class thoroughly. I disagree. In most cases, unit tests should test the behavior of the class, i.e. a block box test should suffice. White box tests, i.e. where you look at the innards of something, are okay, if you are dealing with coarse grained entities like whole modules.
In a class the innards are really just implementation details that may change due to refactoring. You don’t want to have to change your unit tests because you just did an uninteresting minor refactoring, while the visible behavior of the class has not changed at all.
The class is too big
If you are testing a class and have the feeling there is something going on inside the class that is worth testing, then it may be there is too much going on in that class. In that case it may be worth pulling that something out of the class and make it a separate entity with its own unit tests.
For example, if you have implemented a nontrivial algorithm to do something for the class, then the algorithm should be factored out, unless the class is the algorithm. That becomes apparent if you consider the single responsibility principle: If the class is more than just the algorithm, then that “more” is the class’ single responsibility.
Defining the algorithm in detail is another responsibility that should lay elsewhere, so the class can just use the algorithm. Therefore, put the algorithm in its own class or its own set of functions. In other words, make the algorithm another unit with its own public interface and its own unit tests.
Unreachable code
Some people are really crazy about reaching 100% test coverage. They declare unit tests as friends so they can get code paths under test that are inaccessible from the public interface in normal situations.
Some of those code paths are for situations that can happen but are hard to simulate in unit tests, e.g. network failures, lost database connections or rogue hardware. Make those code paths as short and simple as possible, prove them to be right and forget the unit tests. 99% or 98% coverage is perfectly ok.
More often than those special cases I see code that can not be reached in production at all. A prominent example are null pointer checks: If all your public interface functions check for null pointers, you can safely pass them to a private function without having to check for null pointers in that function again.
The null pointer case in that function need not be tested at all, you can delete it, and your coverage goes up. (You might want to replace that pointer parameter by a reference).
Don’t cheat, though
Of course that does not mean you should remove friend declarations and just make some private functions public so your unit tests can access them. That also does not mean you should add getter functions for all member variables to be able to read them in your unit tests. Remember, your unit tests should not invade the production code.
Conclusion
`friend` declarations should be used with care in general. Unit tests should not need to be `friend`s at all.
Avoid making unit tests `friend` of any class.
Permalink
This is a great topic!
I agree with the philosophy to treat the class as a black-box with your unit tests.
To state one of your points in a slightly different way:
If you believe you need to test private functions in a class, then factor that function (algorithm, object, etc…) out of the class and verify with a different test suite.
Thanks for the post.
Permalink
Good advice!
I agree that 100% coverage on unit tests isn’t necessary, but I think you should try to get 100% test coverage. If it isn’t covered by a unit test, it should be covered by an integration test or an acceptance test. If you can’t get a line of code to execute, maybe the system doesn’t work the way you think it does.