Sugerowałbym trochę przeredagować kod. Kiedy musisz zacząć zastanawiać się nad użyciem refleksji lub innego rodzaju rzeczy, do testowania kodu coś jest nie tak.
Wspomniałeś o różnych rodzajach problemów. Zacznijmy od prywatnych pól. W przypadku pól prywatnych dodałbym nowy konstruktor i do niego wstrzyknąłem pola. Zamiast tego:
public class ClassToTest {
private final String first = "first";
private final List<String> second = new ArrayList<>();
...
}
Użyłbym tego:
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;
}
...
}
Nie będzie to problemem nawet w przypadku niektórych starszych kodów. Stary kod będzie używał pustego konstruktora, a jeśli zapytasz mnie, zrefaktoryzowany kod będzie wyglądał na czystszy, a będziesz mógł wstrzyknąć niezbędne wartości w teście bez refleksji.
Teraz o metodach prywatnych. Z mojego osobistego doświadczenia, kiedy musisz wprowadzić prywatną metodę do testowania, ta metoda nie ma nic wspólnego z tą klasą. Typowym wzorcem w takim przypadku byłoby zawinięcie go w interfejs, na przykład, Callable
a następnie przekazanie tego interfejsu również w konstruktorze (z tą sztuczką wielokrotnego konstruktora):
public ClassToTest() {
this(...);
}
public ClassToTest(final Callable<T> privateMethodLogic) {
this.privateMethodLogic = privateMethodLogic;
}
Przeważnie wszystko, co napisałem, wygląda na wzór wstrzyknięcia zależności. Z mojego osobistego doświadczenia jest to bardzo przydatne podczas testowania i myślę, że ten rodzaj kodu jest czystszy i łatwiejszy w utrzymaniu. To samo powiedziałbym o klasach zagnieżdżonych. Jeśli zagnieżdżona klasa zawiera ciężką logikę, lepiej byłoby przenieść ją jako prywatną klasę pakietu i wstrzyknąć do klasy, która tego potrzebuje.
Istnieje również kilka innych wzorców projektowych, których użyłem podczas refaktoryzacji i utrzymywania starszego kodu, ale wszystko zależy od przypadków testowania twojego kodu. Korzystanie z refleksji w większości nie stanowi problemu, ale jeśli masz mocno przetestowaną aplikację korporacyjną i testy są uruchamiane przed każdym wdrożeniem, wszystko staje się bardzo wolne (to po prostu denerwujące i nie lubię tego typu rzeczy).
Jest też zastrzyk setera, ale nie zalecałbym go używać. Lepiej trzymam się konstruktora i inicjuję wszystko, gdy jest to naprawdę konieczne, pozostawiając możliwość wstrzyknięcia niezbędnych zależności.