DEV Community

Cover image for Testing the untestable
Nicolas Fränkel
Nicolas Fränkel

Posted on • Originally published at blog.frankel.ch

Testing the untestable

I'm currently working on a software designed more than a decade ago. It offers a plugin architecture: you can develop a plugin whose lifecycle is handled by the software. The tough part, though, is how you access the platform capabilities: via static methods on singletons.

@Override
public boolean start() {
    var aService = AService.getInstance();
    var anotherService = AnotherService.getInstance();
    // Do something with the services
    var result = ...;
    return result;
}
Enter fullscreen mode Exit fullscreen mode

There's no easy way to test the start() method. In the old days, Mockito developers had pushed back against this feature, and the only alternative was PowerMock. The decision was reversed in 2020 with the 3.4.0 release, which introduced static method mocking in Mockito.

I liked the previous situation better. My opinion is that having to mock static methods is a sign of badly designed code. I wrote about it already ten years ago. With PowerMock, one could mock the static methods, write the test, redesign the code, and then remove PowerMock. With the current situation, one can't look at the dependencies to search for design smells. In any case, the above problem still stands, and I can't change the design. It's forced upon me.

The solution is strangely straightforward, though. Just write a wrapper method around the one:

@VisibleForTesting                                                 //1
boolean start(AService aService, AnotherService anotherService) {  //2
    // Do something with the services
    var result = ...;
    return result;
}

@Override
public boolean start() {
    var aService = AService.getInstance();
    var anotherService = AnotherService.getInstance();
    return start(aService, anotherService);                        //3
}
Enter fullscreen mode Exit fullscreen mode
  1. Method is normally private, but since we want to test it, we make it package visible. The @VisibleForTesting annotation is for documentation purposes.
  2. The testable method has parameters that can be mocked
  3. Call the testable method

In this post, I showed how one can test legacy code not built on Dependency Injection.
This is a pretty straightforward way to test untestable code.


Originally published at A Java Geek on October 19th, 2025

Top comments (2)

Collapse
 
xwero profile image
david duymelinck • Edited

Doesn't that go against the advise to test private methods through public method behavior?
Wouldn't it be better to have an overloaded start method that accepts the services, and test that?
Then you have created an upgrade path for the poorly designed code.
For me @VisibleForTesting seems like half of the way to better code.

Collapse
 
nfrankel profile image
Nicolas Fränkel

There's no private method to test, but a public untestable one at the unit level. I don't understand your comment.