As my software engineering class progresses I’m gradually bringing back old habits of writing unit tests and carefully designing class methods to be unit-testable. Yesterday as I was doing a lab dealing with pointers (we’re learning C++), I ran into an issue which I think might be a serious problem in the weeks to come. We’re using the CppUnit testing framework and CppUnit won’t let me test private methods. I haven’t seriously looked around to see if there is a workaround because there’s a more important question here lying under the obvious “how”: should private methods be directly tested at all?
A quick Google search reveals lots of opinion both for and against unit testing private methods. The important arguments against are:
- Testing private methods breaks encapsulation and hinders the developer’s ability to refactor even if the external interface is preserved. This has effects on the efficiency of your software production cycle.
- If testing the external interface (via public methods) doesn’t adequately exercise private code, then why is the private code there?
- If you are convinced that the private methods need to be tested, that can be a sign that the private code needs to be refactored into a separate class.
Proponents of private testing will offer the following arguments:
- Anything that can break should be tested.
- If you’re going through an interface to test something beneath it, that’s not really unit testing, is it? (I actually just thought of that right now, not sure if it has come up before.)
- Testing should be fast and give precise feedback. Having to go through intermediate methods can slow down the tests and more importantly can make the source of failure ambiguous.
I’m sure there are other more nuanced arguments on both sides, but I think this gives a fair overview of the situation. As you can see, it’s not a simple case of practicality vs theoretical purity. Both sides have practical and theoretical arguments.
I am mostly a proponent of the practical side of things. I believe that rules and frameworks should be designed to enhance our productivity and at the same time, we need some rules in place to make sure that we are performing as best we can. With such a mindset, a decision isn’t easy. On the one hand, I would like all of my code to be tested at as fine grained a level as is reasonable and I should be able to find errors on an equal level of granularity. The minimum level of granularity I’m ready to accept is the method level. That means that every method should be tested individually and when a test fails it should unambiguously point me to the faulty method. If I need finer detail than that I can pull out a debugger.
But at the same time, I highly value my professional freedom. When I agree to write a piece of code to do something, I should be given maximum freedom within the constraints of working as part of a team. That’s not an excuse to write faulty or obfuscated code because other people will almost certainly be using and/or maintaining it. However, I should not have to go through a lengthy test-passing-approval-getting process everytime I refactor a method into two or more smaller ones. That of course is argument #1 against testing private methods.
Since I hold practicality in such high esteem, I think the need to testing anything that can break is of paramount importance. If your code can break, you need to know about it as soon as possible, encapsulation be damned. It’s not that I’m not outright rejecting against arguments #2 and #3 (I’ll get back to them later) I’m just saying that I hold ‘for’ argument #1 as paramount. With that in mind, unit testing private methods becomes necessary and I need to find a way around the objections. To solve #1, perhaps what is needed is a slightly higher level look at the problem. Tests that exercise your code fall into one of two categories:
- Tests that you’ve written yourself for your own code.
- Tests that other people have written for your code.
I know that many large software houses have employees whose job it is to write to test code, but even if you know for certain that there will be other people testing your code, you should write your tests. By doing so you can take a swing at objection #1. Tests written by the team’s testers will verify the public interface and make sure (hopefully) that your code as a whole does what it’s supposed to do. But your own tests will make sure that the internals of your code are safe. Here you can test for yourself any edges cases that the public interface might let slip through (for various reasons). Since you control both the tests and the code, you can decide when some tests no longer make sense and remove them accordingly.
Of course, this isn’t a bulletproof solution to the problem. There are all the potential problems associated with writing your own tests: it takes a certain amount of discipline and dettachment to write stringent tests and it can be tempting to just write tests that you know your code will pass. It’s up to you as a developer to develop the proper mental attitude. Plus there is always the mental overhead of actually writing and maintaining your own tests in addition to your code. You have to decide for yourself if you’re up to the job.
Coming back to the original list of objections to private testing, #2 and #3 are still unanswered. In fact I don’t think they really have to be ‘answered’, because they point to more underlying issues. #2 raises the question of simplicity. If your code does more than it needs to, that opens up the door to bugs that could easily be avoided. If external callers will never actually call some part of your code, you really should consider leaving it out. #3 is a derivative of object oriented design: if you need to be calling code regularly from an external source, make it it’s own class rather than nesting it layers of accessor code. The logic of this argument is undeniable, but every once in a while (and probably more often) you need to sacrifice the purity of OO for something that is more convenient. That being said, I would still very much encourage you to be careful about where your code is and why it’s there. Good design can make your life as a programmer much easier.
So the final verdict (for me at least) stands thus: test your private methods. However you should not use this as an excuse to write ill-designed code in the hopes that testing will catch the bugs. Testing should be applied to catch any errors that have not been eliminated by careful design, not as an end-of-cycle precaution in the hopes that everything does what it’s supposed to. Use private method testing proactively to pinpoint and eliminate hard to see and unexpected bugs, not as a blanket measure against anything and everything that can go wrong. If your code passes public testing but fails private testing (or vice versa) it’s still bad code and everything that can break, will break. If you’re going to use tests at all, respect what they tell you.
Of course getting your framework to allow private testing without badly abusing object-orientation is another matter altogether. And I haven’t even started looking for an answer to that one.