J'écris un scénario de test pour une classe qui a une injection à 2 niveaux de dépendance. J'utilise l'annotation @Spy pour l'objet d'injection de dépendance à 1 niveau et j'aimerais simuler le deuxième niveau d'injection. Cependant, je continuais à avoir une exception de pointeur nulle au 2e niveau. Existe-t-il un moyen d'injecter la maquette dans l'objet @Spy?
public class CarTestCase{
@Mock
private Configuration configuration;
@Spy
private Engine engine;
@InjectMocks
private Car car;
@Test
public void test(){
Mockito.when(configuration.getProperties("")).return("Something");
car.drive();
}
}
public class Car{
@Inject
private Engine engine;
public void drive(){
engine.start();
}
}
public class Engine{
@Inject
private Configuration configuration;
public void start(){
configuration.getProperties(); // null pointer exception
}
}
Mockito ne peut pas effectuer des injections aussi délicates car ce n'est pas un framework d'injection. Donc, vous devez refactoriser votre code pour le rendre plus testable. C'est facile à faire en utilisant l'injection de constructeur:
public class Engine{
private Configuration configuration;
@Inject
public Engine(Configuration configuration) {
this.configuration = configuration;
}
........
}
public class Car{
private Engine engine;
@Inject
public Car(Engine engine) {
this.engine = engine;
}
}
Dans ce cas, vous devez gérer manuellement les moqueries et les injections:
public class CarTestCase{
private Configuration configuration;
private Engine engine;
private Car car;
@Before
public void setUp(){
configuration = mock(Configuration.class);
engine = spy(new Engine(configuration));
car = new Car(engine);
}
@Test
public void test(){
Mockito.when(configuration.getProperties("")).return("Something");
car.drive();
}
}
Je me suis aussi demandé comment injecter une maquette dans un espion.
L'approche suivante va pas fonctionne:
@Spy
@InjectMocks
private MySpy spy;
Mais le comportement souhaité peut être obtenu par une approche "hybride", en utilisant à la fois l'annotation et la simulation manuelle. Ce qui suit fonctionne parfaitement:
@Mock
private NeedToBeMocked needToBeMocked;
@InjectMocks
private MySpy mySpy;
@InjectMocks
private SubjectUnderTest sut;
@BeforeMethod
public void setUp() {
mySpy = Mockito.spy(new MySpy());
MockitoAnnotations.initMocks(this);
}
(SubjectUnderTest
dépend ici de MySpy
, et MySpy
dépend à son tour de NeedToBeMocked
).
UPD: Personnellement, je pense que si vous devez faire une telle magie trop souvent, cela pourrait être un signe qu'il y a un problème avec les dépendances entre vos classes et qu'il vaut la peine d'effectuer un peu de refactoring pour améliorer votre code.
J'ai également rencontré ce problème lors des tests unitaires avec l'infrastructure de démarrage Spring, mais j'ai trouvé une solution pour utiliser @Spy et @InjectMocks.
Réponse précédente de Yoory N.
@Spy
@InjectMocks
private MySpy spy;
Comme InjectMocks doit avoir une instance créée, la solution fonctionne pour moi comme ci-dessous,
@Spy
@InjectMocks
private MySpy spy = new MySpy();
Je pense que je viens de trouver la réponse définitive. J'ai essayé l'approche Yoory mais changé l'ordre des annotations:
@InjectMocks
@Spy
private MySpy spy;
Je suppose que Mockito crée d'abord la maquette et ajoute un espion en plus de cela. Il n'est donc pas nécessaire d'instancier l'objet MySpy.