Best practices for testing private methods
It is good practice to break down the logic of complex public methods into private methods. For instance:
public class Palindrome {
private final Path inputFile;
public Palindrome(Path inputFile) {
this.inputFile = inputFile;
}
public boolean check() {
return Files.lines(inputFile)
.stream()
.allMatch(s -> new StringBuilder(s).reverse().toString().equals(s));
}
}
After splitting out the palindrome check for a single line, we have:
public class Palindrome {
private final Path inputFile;
public Palindrome(Path inputFile) {
this.inputFile = inputFile;
}
public boolean check() {
return Files.lines(inputFile).stream().allMatch(this::isPalindrome);
}
private boolean isPalindrome(String s) {
return new StringBuilder(s).reverse().toString().equals(s);
}
}
This looks much more approachable now. The private method could even be made static in this case. However, for testing the logic in this private method we have introduced another obstacle because they cannot be directly called from test cases anymore. Using reflection to call private methods would result in brittle test code that is hard to automatically refactor and is thus not recommended.
The recommended approach is to
make the methods package-private and
annotate them with
@VisibleForTesting
.
This annotation is available from several libraries such as Guava, Jetbrains Annotations and Assert4J.
It doesn't matter which of these libraries we choose; so, let's add the following to our build configuration, for instance:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.4.8-jre</version>
</dependency>
And then apply our recommendation:
public class Palindrome {
private final Path inputFile;
public Palindrome(Path inputFile) {
this.inputFile = inputFile;
}
public boolean check() {
return Files.lines(inputFile).stream().allMatch(this::isPalindrome);
}
@VisibleForTesting
boolean isPalindrome(String s) {
return new StringBuilder(s).reverse().toString().equals(s);
}
}
Now we can easily test the single-line palindrome check without requiring reflection or setting up a test scenario with actual input files.
Last updated
Was this helpful?