1
/
5

Testing Private Methods

Photo by Huzeyfe Turan on Unsplash

Let’s try to answer one of the most common questions in unit testing - how to test private methods?

"One of the biggest misconceptions in unit testing is that when you test a class, you should test each and every method in it" - Vladimir Khorikov


To test private methods you would usually choose one of the following approaches:

  1. Use a more powerful library, like PowerMock, that allows you to test private methods.
    Or use reflection by yourself.
  2. Make the method’s access modifier looser, for example, change privatepublic, which allows you to test the method normally.

You can think of public methods as the class’s APIs and private methods as the implementation details.
As with regular APIs, the implementation is the one that frequently changes, whereas the APIs reminds pretty stable.
In the first approach, the tests know too much about the internal details of a class, and when it happens -the tests become fragile, frequently failing even though the observable behavior of the class does not change.

Tests should not have special privileges, they should interact with the class the same way the clients do.
This allows you to test only the logic that can be executed.
It also allows you to write the private methods in a way that contains only the needed logic - code coverage will tell you what parts of the private method are not covered, and if these parts are unreachable, then consider removing them.
Private methods that contain logic that is never used add more things to maintain and increase the probability that something will break.


But what about the second approach? you can change the private methods to public and the problem is solved.
Let’s see why this approach is even more dangerous than the first one.
The second approach is talking about breaking the class’s encapsulation in order to allow testing of private methods.
We are all familiar with one of OOP’s main concepts, encapsulation, and it is clear to us why it is a bad idea to break it.
Let’s see an example to refresh our memory:

public class Order {

private int id;
private List<OrderItem> orderItems;
private int maxOrderItems;

public void add(OrderItem orderItem) {
boolean canAdd = canAddValidation(orderItems);
if (canAdd) {
addOrderItem(orderItem);
}
}

private boolean canAddValidation(List<OrderItem> orderItems) {
if (!CollectionUtils.isEmpty(orderItems) && orderItems.size() < maxOrderItems) {
return true;
}

return false;
}

private void addOrderItem(OrderItem orderItem) {
if (CollectionUtils.isEmpty(orderItems)) {
orderItems = new ArrayList<>();
}

orderItems.add(orderItem);
}
}

Order contains a public method for adding an OrderItem to it.
Before adding the item, it performs a validation.

We usually want to split big methods into smaller ones to make the code clearer, and here we’re doing exactly that by extracting some of the code into canAddValidation() and addOrderItem() methods.
In this case, the private methods do not stand by themselves, and only have meaning as part of the public method’s execution.

Let’s say we changed the private methods to be public.
Now, other objects can use these methods to perform operations that should be performed only internally by the Order object.
For example, other objects can use addOrderItem() to add OrderItem to Order directly, without any validation, which may cause an invalid state of the Order object.


But what if the private method contains complex business logic that you want to test thoroughly?
This case signals us that there is a missing abstraction in our code, and so instead of making the method public, we should extract it into a separate class.

Let’s say that in the example above canAddValidation() method is very complex and contains many business rules.
We can introduce a new domain concept, AddItemValidator class, and move all the logic there, which allows us to test it directly and thoroughly.

Another reason for extracting a method (any method) to a separate class, is for the sake of the DRY principle.


To summarize,
The answer to the question of how to test private methods is then: nohow!
You should only test the observable behavior of your application and not couple the tests to the implementation details.

In case the private method contains important/complex logic, extract it to a separate class and test it thoroughly.

株式会社ビービット's job postings
3 Likes
3 Likes

Weekly ranking

Show other rankings
Invitation from 株式会社ビービット
If this story triggered your interest, have a chat with the team?