Testing private methods
Published: 2014-02-22 by Lars
One of the questions I often receive from developers starting with unit testing, is how we can test private methods. Here is my perspective on that. There are several ways to approach it:
- Make the method public.
- Extract the private method to its own class where it can be public and therefor tested
- Test the private method only indirectly through another (public) method
- Use reflection to invoke the private method
I prefer method 1) and 2), with 3) as last resort. I don't recommend 4).
1) Make the method public.
Having private methods are useful when you are creating an API for other people or teams to consume. Protecting yourself from calls to low level methods by making them private might allow you more freedom in changing those implementations later. However, often the code we write are not really used by anybody but our own team. In that case it might carry almost zero risk to just make the method public. The good thing about this approach is that it is really simple. Simple + low risk = good.
A variation on this approach is to make the method accessible to the test but not to others. This is only applicable to some programming languages. In C++ you can declare your test a friend of the class under test.
2) Extract the private method to its own class where it can be public and therefor tested.
Assume that we have the private method
A.computeDetails(), which is used by the other methods inside
class A. You can create a new
class B, and move the method to become the public method
B.computeDetails(), maybe together with other private methods and some state that they happen to share. Then inside
class A you would have private member
B A.b, and the code that previously did called
this.computeDetails() will now call
this.b.computeDetails() instead. Now, clients of
class A still cannot invoke
A.b is private, so encapsulation is preserved. But we can write a test for
B.computeDetails() because that method is now public. Choose this method if your class is used by different teams.
3) Test the private method only indirectly through another (public) method
Skip writing a test for the private
A.computeDetails(), it will be tested indirectly through the calls to it when testing the public methods of
class A. This means that the individual tests will be less focused, but that is typically not a big issue in practice.
4) Use reflection to invoke the private method
This is just complicated with no real benefits. Don't do this.
Discuss on Twitter