To test a method you need to execute it, but calling private methods directly can be hard or even impossible, depending on the programming language you use. In this article I?ll primarily speak about Python and Java, but the described techniques surely can be applied to many other languages (but I can?t be sure about all of them).
What is a private method?
Have you ever asked yourself, why do you even need private methods? You need private data to maintain consistency, but why private methods?
Strictly speaking, you don?t. But they still be helpful at least for two major reasons:
- You want to extract some code that works with private data;
- You want to extract some code that doesn?t work with private data, but still doesn?t suit API (because user simple doesn?t care).
While the first one prevents private data corruption, the second merely makes your API cleaner.
Don?t test it
Surprisingly, when I first came to Java programming and googled ?How to test private method in Java?, the most popular answer was something like ?Why do you want to test private methods? Users call public methods, test them instead?.
Well, that sounds crazy for me. I surely can just ignore the fact that a public methods call a private one, just imagine that private method code is inlined, but that means that code of the private method will be tested again and again with every public method that calls the private one.
In the following example you really want to test private method, not the two almost identical public ones.
One can notice that some refactoring can allow me not to test the private method. That?s true and we will talk about it later. But testing _set_status without any changes is still clean and reasonable way to do. I don?t buy this ?don?t test private methods?.
Just call it
The simplest and most straightforward way to call a private method is, you know, call it. And that?s exactly what we do in Python and other languages that has private by convention (e. g. Perl).
To ?just call it? in Java we need to change visibility of the method.
The first way is to make method package private (no access modifier) and put tests into the same package. This is a fairly common practice, but you still might want (or already have) another code structure.
The second way is to make method public. To let people know you still don?t want to call this method you can use @VisibleForTestingannotation from Guava or any other convention you like (that?s actually what Python and Perl do). By the way, IDEA totally understands that annotation and will warn you about using such public method outside of tests.
You can also put a test class inside a tested one (at least in Java), but that doesn?t look great. You have to use the same file for both classes, and your production binaries will actually contain test code.
In some languages reflections will do fine, in Ruby it?s that simple:
But in Java such code is so heavy I gave up on idea of providing an example here. More than this, you have to abandon all cool stuff your favourite IDE does for you.
Personally I prefer the ?just call it? method, and I like to use @VisibleForTesting in Java to make it happen.
But let us talk again about refactoring that allows me avoiding testing private methods (by eliminating them).
The point is to merge all private data and private methods into an object of another class that contains no private methods or data, but put the instance of that class into the only private attribute of the original class. Doesn?t sound simple, but it is, consider examples:
So now you can freely test EntityPrivateData, the only thing that remains private is data attribute. Like I said before, you actually don?t need private methods, only private data.
The described method can be useful not only for testing private methods, but also for more expressive design of your software. You can use any number of such private data classes so they have more sense semantically, not only technically.
For me, the most important thing about this pattern is that it proves that you technically can eliminate all private methods. But I still doubt that it?s reasonable to do it every time, your code can bloat without any significant benefit.
This article was written with the help of Nikolay Rys.