web-dev-qa-db-fra.com

Comment fonctionne Mockito @InjectMocks?

Voici ma question:

J'ai plusieurs classes de services Web pour tester que toutes héritent de leurs méthodes d'un service générique. Plutôt que d'écrire un test unitaire pour chacun, je pense que je peux décomposer la suite de tests par domaines fonctionnels (c'est-à-dire trois groupes de méthodes de test, chacun s'appuyant sur un appel de méthode DAO sous-jacent différent).

Ce que je propose de faire, c'est:

@Mock StateDAO mockedStateDao;
@Mock CountyDAO mockedCountyDao;
@Mock VisitorDAO mockedVisitorDao;

puis appelez:

@InjectMocks CountyServiceImpl<County> countyService = new CountyServiceImpl<County>();
@InjectMocks StateServiceImpl<State> stateService = new StateServiceImpl<State>();
@InjectMocks VisitorServiceImpl<Visitor> visitorService = new VisitorServiceImpl<Visitor>();

Comment puis-je être sûr que chaque mockedDAO sera injecté dans le bon service? Serait-il plus facile de câbler automatiquement les trois (plutôt que d'utiliser @InjectMocks)?

J'utilise Spring, Hibernate et Mockito ...

18
DYezek

Eh bien, la réponse de Nicholas est presque correcte, mais au lieu de deviner, regardez le javadoc de InjectMocks , il contient plus de détails;)

Pour moi, c'est bizarre d'avoir autant de services en un seul test, cela ne me semble pas correct, en tant que test unitaire ou en tant que test d'intégration. Dans le test unitaire, c'est faux parce que vous avez bien trop de collaborateurs, cela ne ressemble pas à un objet (ou SOLID). Dans les tests d'intégration, c'est bizarre parce que le code que vous testez l'intégration avec la base de données ne se moque pas d'elle.

Pour une référence rapide en 1.9.5 vous avez:

Marquez un champ sur lequel l'injection doit être effectuée.

Permet l'injection de raccourci et d'espionnage. Minimise les injections répétitives de simulacres et d'espions. Mockito essaiera d'injecter des simulations uniquement par injection de constructeur, injection de setter ou injection de propriété dans l'ordre et comme décrit ci-dessous. Si l'une des stratégies suivantes échoue, Mockito ne signalera pas l'échec; c'est-à-dire que vous devrez fournir les dépendances vous-même.

  1. Injection de constructeur; le constructeur le plus grand est choisi, puis les arguments sont résolus avec des simulations déclarées dans le test uniquement.

    Remarque: Si les arguments ne peuvent pas être trouvés, alors null est passé. Si des types non moquables sont souhaités, l'injection de constructeur ne se produira pas. Dans ces cas, vous devrez vous-même satisfaire les dépendances.

  2. Injection de setter de propriété; les mocks seront d'abord résolus par type, puis, s'il existe plusieurs propriétés du même type, par la correspondance du nom de propriété et le faux nom.

    Note 1: Si vous avez des propriétés du même type (ou même effacement), il est préférable de nommer tous les champs annotés @Mock avec les propriétés correspondantes, sinon Mockito pourrait être confus et l'injection ne le sera pas se produire.

    Note 2: Si l'instance de @InjectMocks n'a pas été initialisée auparavant et n'a pas de constructeur sans argument, elle sera initialisée avec ce constructeur.

  3. Injection de champ; les mocks seront d'abord résolus par type, puis, s'il existe plusieurs propriétés du même type, par la correspondance du nom de champ et du faux nom.

    Note 1: Si vous avez des champs avec le même type (ou même effacement), il est préférable de nommer tous les champs annotés @Mock avec les champs correspondants, sinon Mockito pourrait être confus et l'injection ne le sera pas se produire.

    Note 2: Si l'instance de @InjectMocks n'a pas été initialisée auparavant et n'a pas de constructeur sans argument, elle sera initialisée avec ce constructeur.

22
Brice

Si vous avez plusieurs services et souhaitez remplacer les DAO par des objets fantômes dans un environnement basé sur Spring, je recommanderais d'utiliser Springockito: https://bitbucket.org/kubek2k/springockito/wiki/Home

qui est également mentionné ici: Injection de Mockito dans un haricot Spring

Votre classe de test pourrait alors ressembler à ceci:

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration (loader = SpringockitoContextLoader.class, locations =    {"classpath:/org/example/package/applicationContext.xml"})
public class NameOfClassTest {

    @Autowired
    @ReplaceWithMock 
    StateDAO mockedStateDao;

    @Autowired
    @ReplaceWithMock 
    CountyDAO mockedCountyDao;

    @Autowired
    @ReplaceWithMock 
    VisitorDAO mockedVisitorDao;

Dans votre @Test ou @Before Methode, vous pouvez configurer vos mocks de la manière standard Mockito:

Mockito.doReturn(null).when(mockedCountyDao).selectFromDB();
3
giesemic

Eh bien, la méthode statique MockitoAnnotations.initMocks(Object) est utilisée pour bootstrap tout le processus.

Je ne sais pas exactement comment cela fonctionne, car je n'ai pas parcouru le code source, mais je l'implémenterais quelque chose comme ceci:

  1. Analyser la classe de Object passée pour les variables membres avec le @Mock annotation.
  2. Pour chacun, créez une maquette de cette classe et définissez-la sur ce membre.
  3. Analyser la classe de Object passée pour les variables membres avec le @InjectMocks annotation.
  4. Analyser la classe de chaque membre trouvé pour les membres qu'il possède qui peuvent être injectés avec l'un des objets fictifs créés dans (2) (c'est-à-dire, où le champ est une classe/interface parent, ou la même classe, comme les objets fictifs déclarés et définissez-le sur ce membre.
1

Peu importe, regardé en ligne - l'annotation InjectMocks traite n'importe quoi avec l'annotation @Mock comme un champ et a une portée statique (à l'échelle de la classe), donc je ne pouvais vraiment pas garantir que les simulations iraient au bon service. il s'agissait en quelque sorte d'une expérience de réflexion pour essayer de faire des tests unitaires au niveau des fonctionnalités plutôt qu'au niveau de la classe. Je suppose que je vais simplement câbler ces trucs avec Spring ...

0
DYezek