I recently had a discussion about about whether or not people should test private methods. One of my colleagues exclaimed that the problem with rule of not testing private methods is that it is essentially tantamount to saying that you should not have anything complex enough to test in your private methods, which on its face would appear to be a bad rule.
My colleagues response illuminated a common and dare I say almost crippling problem with our industry. A failure to properly distinguish between preference, philosophy and fact and properly convey said differences when communicating. A failure to take the time to understand what the other person is saying and why before drawing conclusions.
Testing private private methods is not a rule so much as it is a logical conclusion. One that stems from a fundamental difference in the philosophy of testing.
We do not write tests because the code is complex; inversely and INFINITELY more important, we DO NOT neglect to write tests simply because the code is simple. We write tests as a means to programmatically state and verify system requirements. We test system requirements/specs NOT implementation. Good tests as much as possible refrain directly touching implementation details (and let’s be clear - a private method is the definition of an implementation detail). A excellent test suite should be able to endure significant system extension/refactoring/implementation changes and still run successfully - this in fact is one of the major advantages of a good test suite, the ability to refactor with confidence.
If you’re testing a private method, it means you are doing one of two things:
1) Neglecting to test the actual feature/requirement which said private method was written to fulfill
2) Testing a piece of code that is already being tested via the test written to verify the feature/requirement that said private method was written to fulfill - this is called testing noise.
Both cases are obviously a violation of the philosophy behind testing as stated above. As a result, both are equally dangerous in the long run. While the dangers of the former are obvious, the latter, apart from rapidly making the test suite unwieldy, makes the development team numb to failing tests - tests are failing not because the system isn’t working properly anymore, but just because I cleaned things up and removed this unnecessary private method - and makes developers comfortable deleting tests when they fail.
This is one of the fundamental strengths of TDD. TDD forces you to write tests before implementation, which ensures that the tests focus on the requirements and not implementation. TDD is a process that lends itself to significantly better tests which in turn result in significantly better software.