web-dev-qa-db-fra.com

Mockito Inject mock dans un objet Spy

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
    }

}
12
Wildchild

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();
    }

}
9
Sergii Bishyr

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.

11
Yoory N.

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();
6
Jack Yang

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.

0
DavidBu