In the previous email, I wrote about exposing private methods for the sole purpose of unit testing them.
The gist of that email is that you shouldn’t unit test private methods directly. Instead, test them indirectly, as part of the class’s public API.
If you find that a private method contains important logic that is too complex to be tested through the class’s public API, it’s an indication that this logic should be extracted into a separate class.
I’ve received another interesting comment regarding unit testing private methods that I’d like to elaborate on here.
The comment is about another way to deal with private methods — by exposing them to unit tests indirectly, via reflection.
The idea is basically to have a special library that modifies the methods' accessors such that you can test those methods. Or use reflection in tests to get access to private methods.
The benefit of this approach is that the production code remains the same. The change is only visible to the test code, which means your code’s encapsulation stays intact.
So, is this a good way to test private methods?
This is an improvement over the blunt exposure of private methods, but it still falls short on the metric of resisting to refactoring.
The problem with unit testing private methods is not only the issues with the production code encapsulation. The other problem is that the tests' value decreases significantly in the setting where they bind to implementation details (such as private methods), because such tests start to raise false alarms every time you modify those implementation details.
Here’s the test accuracy formula once again:
Both the signal (the tests' ability to find bugs) and noise (the tests' ability not to raise false alarms) are critically important.
As I wrote in the book, there are 4 components of a good unit test:
Protection against regressions — How much signal the test generates.
Resistance to refactoring — How little noise the test produces after you refactor the underlying production code.
Fast feedback — How fast the test is.
Maintainability — How small the test is and how many out-of-process dependencies it reaches out to.
These four components, when multiplied together, determine the value of a test.
And by multiplied, I mean in a mathematical sense; that is, if a test gets zero in one of the components, its value turns to zero as well:
Value estimate = [0..1] * [0..1] * [0..1] * [0..1]
By targeting your tests at private methods, you reduce the 2nd component (resistance to refactoring) to almost zero. Therefore, the overall value of the test reduces to zero with it.
So again, exposing private methods is not only about the production code encapsulation. It’s also about binding your tests to implementation details, which is never a good idea.
P.S. Have a question about unit testing? Reply to this email — I respond to every message.
Enjoy this message? Here are more things you might like:
Workshops — I offer a 2-day workshop for organizations on Domain-Driven Design and Unit Testing. Reply to this email to discuss.
Unit Testing Principles, Patterns and Practices — A book for people who already have some experience with unit testing and want to bring their skills to the next level.
Learn more »
My Pluralsight courses — The topics include Unit Testing, Domain-Driven Design, and more.
Learn more »