I would suggest you refactoring your code a little bit. When you have to start thinking about using reflection or other kind of stuff, for just testing your code, something is going wrong with your code.
You mentioned different types of problems. Let's start with private fields. In case of private fields I would have added a new constructor and injected fields into that. Instead of this:
public class ClassToTest {
private final String first = "first";
private final List<String> second = new ArrayList<>();
...
}
I'd have used this:
public class ClassToTest {
private final String first;
private final List<String> second;
public ClassToTest() {
this("first", new ArrayList<>());
}
public ClassToTest(final String first, final List<String> second) {
this.first = first;
this.second = second;
}
...
}
This won't be a problem even with some legacy code. Old code will be using an empty constructor, and if you ask me, refactored code will look cleaner, and you'll be able to inject necessary values in test without reflection.
Now about private methods. In my personal experience when you have to stub a private method for testing, then that method has nothing to do in that class. A common pattern, in that case, would be to wrap it within an interface, like Callable
and then you pass in that interface also in the constructor (with that multiple constructor trick):
public ClassToTest() {
this(...);
}
public ClassToTest(final Callable<T> privateMethodLogic) {
this.privateMethodLogic = privateMethodLogic;
}
Mostly all that I wrote looks like it's a dependency injection pattern. In my personal experience it's really useful while testing, and I think that this kind of code is cleaner and will be easier to maintain. I'd say the same about nested classes. If a nested class contains heavy logic it would be better if you'd moved it as a package private class and have injected it into a class needing it.
There are also several other design patterns which I have used while refactoring and maintaining legacy code, but it all depends on cases of your code to test. Using reflection mostly is not a problem, but when you have an enterprise application which is heavily tested and tests are run before every deployment everything gets really slow (it's just annoying and I don't like that kind of stuff).
There is also setter injection, but I wouldn't recommended using it. I'd better stick with a constructor and initialize everything when it's really necessary, leaving the possibility for injecting necessary dependencies.