Je vous suggère de refactoriser un peu votre code. Lorsque vous devez commencer à penser à utiliser la réflexion ou d'autres types de choses, pour simplement tester votre code, quelque chose ne va pas avec votre code.
Vous avez mentionné différents types de problèmes. Commençons par les champs privés. Dans le cas de champs privés, j'aurais ajouté un nouveau constructeur et injecté des champs dans celui-ci. Au lieu de cela:
public class ClassToTest {
private final String first = "first";
private final List<String> second = new ArrayList<>();
...
}
J'aurais utilisé ceci:
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;
}
...
}
Ce ne sera pas un problème, même avec du code hérité. L'ancien code utilisera un constructeur vide, et si vous me le demandez, le code refactorisé sera plus propre et vous pourrez injecter les valeurs nécessaires dans le test sans réflexion.
Maintenant sur les méthodes privées. D'après mon expérience personnelle, lorsque vous devez écraser une méthode privée pour les tests, cette méthode n'a rien à voir dans cette classe. Un modèle commun, dans ce cas, serait de l' envelopper dans une interface, comme Callable
et vous passez ensuite cette interface également dans le constructeur (avec cette astuce de constructeur multiple):
public ClassToTest() {
this(...);
}
public ClassToTest(final Callable<T> privateMethodLogic) {
this.privateMethodLogic = privateMethodLogic;
}
La plupart du temps, tout ce que j'ai écrit ressemble à un schéma d'injection de dépendance. D'après mon expérience personnelle, c'est vraiment utile lors des tests, et je pense que ce type de code est plus propre et sera plus facile à maintenir. Je dirais la même chose des classes imbriquées. Si une classe imbriquée contient une logique lourde, il serait préférable de la déplacer en tant que classe privée de package et de l'injecter dans une classe qui en a besoin.
Il existe également plusieurs autres modèles de conception que j'ai utilisés lors de la refactorisation et de la maintenance du code hérité, mais tout dépend des cas de votre code à tester. L'utilisation de la réflexion n'est généralement pas un problème, mais lorsque vous avez une application d'entreprise qui est fortement testée et que les tests sont exécutés avant chaque déploiement, tout devient vraiment lent (c'est juste ennuyeux et je n'aime pas ce genre de choses).
Il y a aussi l'injection de setter, mais je ne recommanderais pas de l'utiliser. Je préfère m'en tenir à un constructeur et initialiser tout quand c'est vraiment nécessaire, en laissant la possibilité d'injecter les dépendances nécessaires.