Premiers pas avec Mockito @Mock, @Spy, @Captor et @InjectMocks

1. Vue d'ensemble

Dans ce didacticiel, nous aborderons les annotations de la bibliothèque Mockito - @Mock , @Spy , @Captor et @InjectMocks .

Pour plus de bonté Mockito, jetez un œil à la série ici.

2. Activer les annotations Mockito

Avant d'aller plus loin, explorons différentes manières d'activer l'utilisation des annotations avec les tests Mockito.

2.1. MockitoJUnitRunner

La première option que nous avons est d' annoter le test JUnit avec un MockitoJUnitRunner comme dans l'exemple suivant:

@RunWith(MockitoJUnitRunner.class) public class MockitoAnnotationTest { ... }

2.2. MockitoAnnotations.initMocks ()

Alternativement, nous pouvons également activer les annotations Mockito par programme , en appelant MockitoAnnotations.initMocks () :

@Before public void init() { MockitoAnnotations.initMocks(this); }

2.3. MockitoJUnit.rule ()

Enfin, nous pouvons utiliser un MockitoJUnit.rule () comme indiqué ci-dessous:

public class MockitoInitWithMockitoJUnitRuleUnitTest { @Rule public MockitoRule initRule = MockitoJUnit.rule(); ... }

Dans ce cas, nous devons nous rappeler de rendre notre règle publique .

3. Annotation @Mock

L'annotation la plus utilisée dans Mockito est @Mock . Nous pouvons utiliser @Mock pour créer et injecter des instances simulées sans avoir à appeler Mockito.mock manuellement.

Dans l'exemple suivant, nous allons créer une ArrayList simulée avec la méthode manuelle sans utiliser l' annotation @Mock :

@Test public void whenNotUseMockAnnotation_thenCorrect() { List mockList = Mockito.mock(ArrayList.class); mockList.add("one"); Mockito.verify(mockList).add("one"); assertEquals(0, mockList.size()); Mockito.when(mockList.size()).thenReturn(100); assertEquals(100, mockList.size()); }

Et maintenant, nous allons faire la même chose mais nous allons injecter la maquette en utilisant l' annotation @Mock :

@Mock List mockedList; @Test public void whenUseMockAnnotation_thenMockIsInjected() { mockedList.add("one"); Mockito.verify(mockedList).add("one"); assertEquals(0, mockedList.size()); Mockito.when(mockedList.size()).thenReturn(100); assertEquals(100, mockedList.size()); }

Notez comment - dans les deux exemples, nous interagissons avec la maquette et vérifions certaines de ces interactions - juste pour nous assurer que la maquette se comporte correctement.

4. Annotation @Spy

Voyons maintenant comment utiliser l' annotation @Spy pour espionner une instance existante.

Dans l'exemple suivant - nous créons un espion d'une liste à l'ancienne sans utiliser l' annotation @Spy :

@Test public void whenNotUseSpyAnnotation_thenCorrect() { List spyList = Mockito.spy(new ArrayList()); spyList.add("one"); spyList.add("two"); Mockito.verify(spyList).add("one"); Mockito.verify(spyList).add("two"); assertEquals(2, spyList.size()); Mockito.doReturn(100).when(spyList).size(); assertEquals(100, spyList.size()); }

Faisons maintenant de même - espionnons la liste - mais faisons-le en utilisant l' annotation @Spy :

@Spy List spiedList = new ArrayList(); @Test public void whenUseSpyAnnotation_thenSpyIsInjectedCorrectly() { spiedList.add("one"); spiedList.add("two"); Mockito.verify(spiedList).add("one"); Mockito.verify(spiedList).add("two"); assertEquals(2, spiedList.size()); Mockito.doReturn(100).when(spiedList).size(); assertEquals(100, spiedList.size()); }

Notez comment, comme avant - nous interagissons avec l'espion ici pour nous assurer qu'il se comporte correctement. Dans cet exemple, nous:

  • Utilisé la méthode réelle spiedList.add () pour ajouter des éléments à la spiedList .
  • Stubbed la méthode spiedList.size () pour renvoyer 100 au lieu de 2 en utilisant Mockito.doReturn () .

5. Annotation @Captor

Ensuite, voyons comment utiliser l' annotation @Captor pour créer une instance ArgumentCaptor .

Dans l'exemple suivant, nous créons un ArgumentCaptor à l'ancienne sans utiliser l' annotation @Captor :

@Test public void whenNotUseCaptorAnnotation_thenCorrect() { List mockList = Mockito.mock(List.class); ArgumentCaptor arg = ArgumentCaptor.forClass(String.class); mockList.add("one"); Mockito.verify(mockList).add(arg.capture()); assertEquals("one", arg.getValue()); }

Nous allons maintenant faire usage de @Captor dans le même but - pour créer un ArgumentCaptor exemple:

@Mock List mockedList; @Captor ArgumentCaptor argCaptor; @Test public void whenUseCaptorAnnotation_thenTheSam() { mockedList.add("one"); Mockito.verify(mockedList).add(argCaptor.capture()); assertEquals("one", argCaptor.getValue()); }

Remarquez comment le test devient plus simple et plus lisible lorsque nous supprimons la logique de configuration.

6. Annotation @InjectMocks

Voyons maintenant comment utiliser l' annotation @InjectMocks pour injecter automatiquement des champs simulés dans l'objet testé.

Dans l'exemple suivant, nous utilisons @InjectMocks pour injecter le faux wordMap dans le dic MyDictionary :

@Mock Map wordMap; @InjectMocks MyDictionary dic = new MyDictionary(); @Test public void whenUseInjectMocksAnnotation_thenCorrect() { Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning"); assertEquals("aMeaning", dic.getMeaning("aWord")); }

Et voici la classe MyDictionary :

public class MyDictionary { Map wordMap; public MyDictionary() { wordMap = new HashMap(); } public void add(final String word, final String meaning) { wordMap.put(word, meaning); } public String getMeaning(final String word) { return wordMap.get(word); } } 

7. Injecter une maquette dans un espion

Semblable au test ci-dessus, nous pourrions vouloir injecter une simulation dans un espion:

@Mock Map wordMap; @Spy MyDictionary spyDic = new MyDictionary();

Cependant, Mockito ne prend pas en charge l'injection de simulacres dans les espions, et le test suivant aboutit à une exception:

@Test public void whenUseInjectMocksAnnotation_thenCorrect() { Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning"); assertEquals("aMeaning", spyDic.getMeaning("aWord")); }

Si nous voulons utiliser une maquette avec un espion, nous pouvons injecter manuellement la maquette via un constructeur:

MyDictionary(Map wordMap) { this.wordMap = wordMap; }

Au lieu d'utiliser l'annotation, nous pouvons maintenant créer l'espion manuellement:

@Mock Map wordMap; MyDictionary spyDic; @Before public void init() { MockitoAnnotations.initMocks(this); spyDic = Mockito.spy(new MyDictionary(wordMap)); } 

Le test va maintenant réussir.

8. Exécution dans NPE lors de l'utilisation d'annotation

Souvent, nous pouvons rencontrer NullPointerException lorsque nous essayons d'utiliser réellement l'instance annotée avec @Mock ou @Spy :

public class MockitoAnnotationsUninitializedUnitTest { @Mock List mockedList; @Test(expected = NullPointerException.class) public void whenMockitoAnnotationsUninitialized_thenNPEThrown() { Mockito.when(mockedList.size()).thenReturn(1); } }

La plupart du temps, cela se produit simplement parce que nous avons oublié d'activer correctement les annotations Mockito.

Donc, nous devons garder à l'esprit que chaque fois que nous voulons utiliser des annotations Mockito, nous devons faire un pas supplémentaire et les initialiser comme nous l'avons déjà expliqué précédemment.

9. Remarques

Enfin, voici quelques notes sur les annotations Mockito:

  • Les annotations de Mockito minimisent le code de création de simulation répétitif
  • Ils rendent les tests plus lisibles
  • @InjectMocks est nécessaire pour injecter les instances @Spy et @Mock

10. Conclusion

Dans ce tutoriel rapide, nous avons montré les bases des annotations dans la bibliothèque Mockito .

L'implémentation de tous ces exemples peut être trouvée à l'adresse over sur GitHub. Il s'agit d'un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.

Et bien sûr, pour plus de bonté Mockito, jetez un œil à la série ici.