Utilisation de Mockito ArgumentCaptor

1. Vue d'ensemble

Dans ce tutoriel, nous allons couvrir un cas d'utilisation courant de l'utilisation de Mockito ArgumentCaptor dans nos tests unitaires.

Sinon, pour d'autres cas d'utilisation de Mockito.verify , consultez notre livre de recettes Mockito Verify.

2. Utilisation d' ArgumentCaptor

ArgumentCaptor nous permet de capturer un argument passé à une méthode afin de l'inspecter. Ceci est particulièrement utile lorsque nous ne pouvons pas accéder à l'argument en dehors de la méthode que nous aimerions tester.

Par exemple, considérons une classe EmailService avec une méthode d' envoi que nous aimerions tester:

public class EmailService { private DeliveryPlatform platform; public EmailService(DeliveryPlatform platform) { this.platform = platform; } public void send(String to, String subject, String body, boolean html) { Format format = Format.TEXT_ONLY; if (html) { format = Format.HTML; } Email email = new Email(to, subject, body); email.setFormat(format); platform.deliver(email); } ... }

Dans EmailService . send , remarquez comment platform.deliver prend un nouvel e-mail comme argument. Dans le cadre de notre test, nous aimerions vérifier que le champ de format du nouvel e - mail est défini sur Format.HTML . Pour ce faire, nous devons capturer et inspecter l'argument qui est passé à platform.deliver .

Voyons comment nous pouvons utiliser ArgumentCaptor pour nous aider.

2.1. Configurer le test unitaire

Tout d'abord, créons notre classe de test unitaire:

@RunWith(MockitoJUnitRunner.class) public class EmailServiceUnitTest { @Mock DeliveryPlatform platform; @InjectMocks EmailService emailService; ... }

Nous utilisons l' annotation @Mock pour simuler DeliveryPlatform , qui est automatiquement injectée dans notre EmailService avec l' annotation @InjectMocks . Reportez-vous à notre article sur les annotations Mockito pour plus de détails.

2.2. Ajouter un champ ArgumentCaptor

Deuxièmement, ajoutons un nouveau champ ArgumentCaptor de type Email pour stocker notre argument capturé:

@Captor ArgumentCaptor emailCaptor;

2.3. Capturez l'argument

Troisièmement, utilisons Mockito.verify avec ArgumentCaptor pour capturer l' e - mail :

Mockito.verify(platform).deliver(emailCaptor.capture());

Nous pouvons ensuite obtenir la valeur capturée et la stocker en tant que nouvel objet Email :

Email emailCaptorValue = emailCaptor.getValue();

2.4. Inspectez la valeur capturée

Enfin, voyons l'ensemble du test avec une assertion pour inspecter l' objet Email capturé :

@Test public void whenDoesSupportHtml_expectHTMLEmailFormat() { String to = "[email protected]"; String subject = "Using ArgumentCaptor"; String body = "Hey, let'use ArgumentCaptor"; emailService.send(to, subject, body, true); Mockito.verify(platform).deliver(emailCaptor.capture()); Email value = emailCaptor.getValue(); assertEquals(Format.HTML, value.getFormat()); }

3. Éviter le stubbing

Bien que nous puissions utiliser un ArgumentCaptor avec stubbing , nous devrions généralement éviter de le faire. Pour clarifier, dans Mockito, cela signifie généralement éviter d'utiliser un ArgumentCaptor avec Mockito.when . Avec le stubbing, nous devrions utiliser un ArgumentMatcher à la place.

Examinons quelques raisons pour lesquelles nous devrions éviter le stubbing.

3.1. Diminution de la lisibilité des tests

Tout d'abord, considérons un test simple :

Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key"); Mockito.when(platform.authenticate(Mockito.eq(credentials))) .thenReturn(AuthenticationStatus.AUTHENTICATED); assertTrue(emailService.authenticatedSuccessfully(credentials));

Ici, nous utilisons Mockito.eq (informations d'identification) pour spécifier quand la maquette doit renvoyer un objet.

Ensuite, considérez le même test en utilisant un ArgumentCaptor à la place:

Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key"); Mockito.when(platform.authenticate(credentialsCaptor.capture())) .thenReturn(AuthenticationStatus.AUTHENTICATED); assertTrue(emailService.authenticatedSuccessfully(credentials)); assertEquals(credentials, credentialsCaptor.getValue());

Contrairement au premier test, remarquez comment nous devons effectuer une assertion supplémentaire sur la dernière ligne pour faire la même chose que Mockito.eq (informations d'identification) .

Enfin, remarquez qu'il n'est pas immédiatement clair à quoi se réfère credentialsCaptor.capture () . C'est parce que nous devons créer le capteur en dehors de la ligne sur laquelle nous l'utilisons, ce qui réduit la lisibilité.

3.2. Localisation réduite des défauts

Une autre raison est que si emailService.authenticatedSuccessfully n'appelle pas platform.authenticate , nous obtiendrons une exception:

org.mockito.exceptions.base.MockitoException: No argument value was captured!

C'est parce que notre méthode stubbed n'a pas capturé d'argument. Cependant, le problème réel ne réside pas dans notre test lui-même, mais dans la méthode réelle que nous testons.

En d'autres termes, cela nous dirige vers une exception dans le test, alors que le défaut réel se trouve dans la méthode que nous testons.

4. Conclusion

Dans ce court didacticiel, nous avons examiné un cas d'utilisation général de l'utilisation d' ArgumentCaptor . Nous avons également examiné les raisons d'éviter d'utiliser ArgumentCaptor avec le stubbing. Comme d'habitude, tous nos exemples de code sont disponibles à l'adresse over sur GitHub.