Test Everything, but not Private Methods
Stumbling onto Ron Jeffries’ blog about testing everything but not accessors I was reminded of a question I get asked surprisingly often – how can I test private methods in [programming language]? When somebody pops this question I always say, you shouldn’t test private methods. And you shouldn’t.
The conversation then typically continues with a “But…” and I end up elaborating on why I say that. This time I’ll elaborate in the Internet, which hopefully helps spread this thought.
“I want to test a private method”
The fundamental motivation for wanting to test private methods is fear. We fear that our code coverage report’s 100% result isn’t the whole truth (and it isn’t) and we don’t trust our unit tests – the ones that already test the private method indirectly – to have kept out all critters from that piece of code encapsulated behind the magic keyword private.
Why don’t we trust our tests? First of all, in most of the cases when this question is popped the code hasn’t been written test-first. Tests that are written afterward tend to suffer from confirmation bias – we write them to prove to ourselves that we are indeed the great programming geniuses who never make a mistake. Such tests can indeed be much less than a full-cover insurance.
The code is also to blame for our lack of trust. We rationalize our need to write direct tests for a private method by pointing out how hideously long, complex and ugly that method is. This line of thinking exhibits itself quite clearly when people respond to my strict statement with, “…but you just said that we should test ‘everything that could possibly break’ and this private method here looks so ugly that it could break at any time!”
“I understand. And you should not test that method if it’s private.”
In this phase of the dialogue we’ve typically establish one thing: the person asking the question really wants to test that private method, usually for the reasons mentioned above. And that is perfectly reasonable. Yet, I persist and restate that they should not write tests for that private method. So what am I thinking? Why shouldn’t we test it directly?
For one, writing tests for a private method might not be technically possible in your chosen programming language without obnoxious trickery like invoking a private method through Java’s Reflection API. Such tests are also bound to break when you swoosh ahead with those lightning-fast refactorings your IDE vendor has graciously automated for you – because referring to methods and classes through literal strings makes the IDE blind to that link, effectively cutting your tests off from the reach of the Rename Method refactoring you just pulled off in Eclipse.
Second, private methods are details that you clearly don’t want to expose through the class’s public API. After all, if you’d be OK with making that private method public then you wouldn’t have asked the question in the first place. Package private and protected methods are a bit of a gray area in this regard. Bumping up the private method’s visibility so that the tests can invoke it but not so much as to make it part of the public API kind of solves the problem of resorting to ugly hacks – you can now invoke the method without reflection. However, the artery is still exposed and your class’s internals are leaking on the floor. Your tests are testing the object’s implementation, not its intended behavior.
“So what would you do?”
At this point we’ve usually established that the method in question is covered indirectly by other tests but that it’s so complex or important that you want to test it directly. So what would I do?
I would test it as a public method on another class.
The whole pain of seeing that private method, wondering whether it truly works or not, and struggling to decide whether to leave it like it is, whether to increase its visibility and test it directly, or whether to resort to the mortal sin of reflection – all of this pain is trying to tell us something. The code is trying to tell us something.
Clearly that piece of code is important and essential enough that it should get a new home and become a public method on some other class – quite possibly on a whole new class. Writing direct tests for it over there would not be an issue anymore.
A place for everything and everything in its place. It seems that Mr. Franklin was a programmer himself.