Différence entre les méthodes when () et doXxx () dans Mockito

1. Introduction

Mockito est un framework de simulation Java populaire. Avec lui, il est simple de créer des objets simulés, de configurer un comportement simulé, de capturer des arguments de méthode et de vérifier les interactions avec des simulacres.

Maintenant, nous allons nous concentrer sur la spécification du comportement simulé. Nous avons deux façons de faire cela: la syntaxe when (). ThenDoSomething () et doSomething (). When () .

Dans ce court tutoriel, nous verrons pourquoi nous les avons tous les deux.

2. Méthode when ()

Considérons l' interface Employé suivante :

interface Employee { String greet(); void work(DayOfWeek day); }

Dans nos tests, nous utilisons une maquette de cette interface. Disons que nous voulons configurer la méthode de la simulation greet () pour renvoyer la chaîne «Hello» . C'est simple de le faire en utilisant la méthode when () de Mockito :

@Test void givenNonVoidMethod_callingWhen_shouldConfigureBehavior() { // given when(employee.greet()).thenReturn("Hello"); // when String greeting = employee.greet(); // then assertThat(greeting, is("Hello")); }

Ce qui se produit? L' objet employé est un simulacre. Lorsque nous appelons l'une de ses méthodes, Mockito enregistre cet appel. Avec l'appel de la méthode when () , Mockito sait que cet appel n'était pas une interaction par la logique métier. C'était une déclaration que nous voulons attribuer un certain comportement à l'objet simulé. Après cela, avec l'une des méthodes thenXxx () , nous spécifions le comportement attendu.

Jusque-là, c'est de la bonne vieille moquerie. De même, nous voulons configurer la méthode work () pour lancer une exception, lorsque nous l'appelons avec un argument de dimanche:

@Test void givenVoidMethod_callingWhen_wontCompile() { // given when(employee.work(DayOfWeek.SUNDAY)).thenThrow(new IAmOnHolidayException()); // when Executable workCall = () -> employee.work(DayOfWeek.SUNDAY); // then assertThrows(IAmOnHolidayException.class, workCall); }

Malheureusement, ce code ne se compilera pas, car dans l' appel work (employee.work (…)) , la méthode work () a un type de retour void ; par conséquent, nous ne pouvons pas l'envelopper dans un autre appel de méthode. Cela signifie-t-il que nous ne pouvons pas nous moquer des méthodes vides? Bien sûr on peut. méthodes doXxx à la rescousse!

3. Méthodes doXxx ()

Voyons comment nous pouvons configurer la levée d' exception avec la méthode doThrow () :

@Test void givenVoidMethod_callingDoThrow_shouldConfigureBehavior() { // given doThrow(new IAmOnHolidayException()).when(employee).work(DayOfWeek.SUNDAY); // when Executable workCall = () -> employee.work(DayOfWeek.SUNDAY); // then assertThrows(IAmOnHolidayException.class, workCall); }

Cette syntaxe est légèrement différente de la précédente: nous n'essayons pas d'encapsuler un appel de méthode void dans un autre appel de méthode. Par conséquent, ce code se compile.

Voyons ce qui vient de se passer. Premièrement, nous avons déclaré que nous voulions lever une exception. Ensuite, nous avons appelé la méthode when () et nous avons passé l'objet simulé. Après cela, nous avons spécifié le comportement de l'interaction fictive que nous voulons configurer.

Notez que ce n'est pas la même méthode when () que nous avons utilisée auparavant. Notez également que nous avons enchaîné l'interaction fictive après l'invocation de when (). Pendant ce temps, nous l'avons défini entre parenthèses avec la première syntaxe.

Pourquoi avons-nous le premier when (). ThenXxx () , quand il n'est pas capable d'une tâche aussi courante, comme la configuration d'un appel void ? Elle présente plusieurs avantages par rapport à la syntaxe doXxx (). When () .

Premièrement, il est plus logique pour les développeurs d'écrire et de lire des déclarations telles que «quand une interaction, puis faire quelque chose» que «faire quelque chose, quand une interaction».

Deuxièmement, nous pouvons ajouter plusieurs comportements à la même interaction avec le chaînage. C'est parce que when () renvoie une instance de la classe OngoingStubbing , dont les méthodes thenXxx () renvoient le même type.

D'un autre côté, les méthodes doXxx () renvoient une instance de Stubber et Stubber.when (T mock) renvoie T , nous pouvons donc spécifier le type d'invocation de méthode que nous voulons configurer. Mais T fait partie de notre application, par exemple, Employee dans nos extraits de code. Mais T ne retournera pas une classe Mockito, nous ne pourrons donc pas ajouter plusieurs comportements avec chaînage.

4. BDDMockito

BDDMockito utilise une syntaxe alternative à celles que nous avons couvertes. C'est assez simple: dans nos configurations simulées, nous devons remplacer le mot-clé « quand» par « donné » et le mot-clé « faire » par « volonté ». À part cela, notre code reste le même:

@Test void givenNonVoidMethod_callingGiven_shouldConfigureBehavior() { // given given(employee.greet()).willReturn("Hello"); // when String greeting = employee.greet(); // then assertThat(greeting, is("Hello")); } @Test void givenVoidMethod_callingWillThrow_shouldConfigureBehavior() { // given willThrow(new IAmOnHolidayException()).given(employee).work(DayOfWeek.SUNDAY); // when Executable workCall = () -> employee.work(DayOfWeek.SUNDAY); // then assertThrows(IAmOnHolidayException.class, workCall); }

5. Conclusion

Nous avons vu les avantages et les inconvénients de la configuration d'un objet simulé de la manière when (). ThenXxx () ou doXxx (). When () . En outre, nous avons vu comment ces syntaxes fonctionnent et pourquoi nous avons les deux.

Comme d'habitude, les exemples sont disponibles à l'adresse over sur GitHub.