web-dev-qa-db-fra.com

Mockito: Injecte des objets réels dans des champs privés @Autowired

J'utilise les annotations @Mock et @InjectMocks de Mockito pour injecter des dépendances dans des champs privés annotés avec le @Autowired de Spring:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Mock
    private SomeService service;

    @InjectMocks
    private Demo demo;

    /* ... */
}

et

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

Maintenant, je voudrais également injecter des objets réels dans des champs privés @Autowired (sans les paramètres). Est-ce possible ou le mécanisme est-il limité à l'injection de Mock?

160
user2286693

Utiliser l'annotation @Spy

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Spy
    private SomeService service = new RealServiceImpl();

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockito considérera tous les champs portant l'annotation @Mock ou @Spy comme candidats potentiels à injecter dans l'instance annotée avec l'annotation @InjectMocks. Dans le cas ci-dessus, l'instance 'RealServiceImpl' sera injectée dans la 'démo'

Pour plus de détails se référer

Mockito-home

@ Spy

@ Mock

253
Dev Blanked

En plus de la réponse @Dev Blanked, si vous souhaitez utiliser un bean existant créé par Spring, le code peut être modifié pour:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {

    @Inject
    private ApplicationContext ctx;

    @Spy
    private SomeService service;

    @InjectMocks
    private Demo demo;

    @Before
    public void setUp(){
        service = ctx.getBean(SomeService.class);
    }

    /* ... */
}

De cette façon, vous n'avez pas besoin de changer votre code (ajouter un autre constructeur) simplement pour que les tests fonctionnent.

16
Yoaz Menda

Mockito n'est pas un framework DI et même les frameworks DI encouragent les injections de constructeur par rapport aux injections sur le terrain.
Vous venez donc de déclarer à un constructeur de définir les dépendances de la classe sous test:

@Mock
private SomeService serviceMock;

private Demo demo;

/* ... */
@BeforeEach
public void beforeEach(){
   demo = new Demo(serviceMock);
}

Utiliser Mockito spy pour le cas général est un très mauvais conseil. Cela rend la classe de test fragile, non linéaire et sujette aux erreurs: de quoi se moque-t-on réellement? Qu'est-ce qui est vraiment testé?
@InjectMocks et @Spy nuisent également à la conception dans son ensemble, car ils encouragent les classes surchargées et les responsabilités mixtes dans les classes.
S'il vous plaît lisez la spy() javadoc avant de l'utiliser à l'aveuglette (les italiques ne m'appartiennent pas):

Crée un espion de l'objet réel. L'espion appelle les méthodes réelles à moins qu'elles ne soient écrites. Les vrais espions doivent être utilisés avec précaution et occasionnellement , par exemple pour traiter du code hérité.

Comme d'habitude, vous allez lire le partial mock warning: La programmation orientée objet s'attaque à la complexité en la divisant en objets SRPy distincts et spécifiques. Comment la maquette partielle s'inscrit-elle dans ce paradigme? Eh bien, ça ne veut tout simplement pas ... Une simulation partielle signifie généralement que la complexité a été déplacée vers une méthode différente sur le même objet. Dans la plupart des cas, ce n'est pas comme cela que vous voulez concevoir votre application.

Cependant, il existe de rares cas où des simulacres partiels sont utiles: vous ne pouvez pas modifier facilement le code (interfaces tierces, refactoring provisoire du code hérité, etc.). Cependant, je n'utiliserais pas de simulacres partiels pour de code conçu.

0
davidxxx