Mockito – Guide d’utilisation

Guide d’utilisation Mockito :

Mockito en dépendance Maven

  <dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.9.5</version>
    <scope>test</scope>
  </dependency>

Mocker les classes

A la main :

ABC abc = Mockito.mock(ABC.class);

Via les annotations :

@RunWith(MockitoJUnitRunner.class)
public class ABCServiceTest {

  @InjectMocks
  private ABCService abcService;
  @Mock
  private ABCCalcul abcCalcul;
  @Mock
  private ABCDAO abcDAO;

}

, avec :

  • @Mock : pour mocker la classe de la propriété qui suit cette annotation, équivalent à private ABC abc = Mockito.mock(ABC.class)
  • @InjectMocks : pour instancier la propriété qui suit cette annotation et lui injecter les classes qui ont été mockées via @Mock

C’est équivalent au code suivant :

	private MeteoService meteoService;
	private MeteoPrediction meteoPrediction;
	private MeteoDAO meteoDAO;

	@Before
	public void before() {
		meteoPrediction = mock(MeteoPrediction.class);
		meteoDAO = mock(MeteoDAO.class);

		meteoService = new MeteoService();

		meteoService.setMeteoPrediction(meteoPrediction);
		meteoService.setMeteoDAO(meteoDAO);
	}

Définir un comportement

La façon de définir un comportement avec Mockito sur une méthode d’un mock dépend du fait que cette méthode est de type ‘void’ ou retourne une valeur.

Dans le cas où la méthode est de type ‘void’, on écrit le comportement de la façon suivante :

Mockito.doABC().when(mock).methode(paramètre);


, avec :

  • doABC(): à remplacer par :
    • doThrow(exception): pour lever une exception
    • doNothing(): pour indiquer qu’aucune action n’est faite par la méthode. (C’est le comportement par défaut)

Dans le cas où la méthode n’est pas de type ‘void’, c’est a dire qui retourne une valeur, nous définissons le comportement de la façon suivante :

Mockito.when(mock.methode(parametres)).thenABC();


, avec :

  • thenABC(): à remplacer par :
    • thenThrow(exception): pour lever une exception
    • thenReturn(valeur): pour indiquer que la méthode retourne toujours cette valeur.

Gestion par défaut des arguments par Mockito

Mockito se base par défaut sur la méthode equals de l’objet passé en argument pour savoir si cet argument est égal à celui qui avait utilisé lors de la définition du comportement.

Lever une exception lors de l’appel de la méthode

Pour une méthode avec valeur de retour :

Mockito.when(meteo.getTemperature(2)).thenThrow(exception);

Pour une méthode sans valeur de retour :

Mockito.doThrow(exception).when(meteo).saveTemperature(2, 9);

Valeur retournée par la méthode

Définir la valeur qui sera retournée par la méthode :

Mockito.when(meteo.getTemperature(1)).thenReturn(10);

Méthode sans valeur de retour

Pour une méthode sans valeur de retour, on peut vérifier qu’elle a bien été appelée par la méthode testée à l’aide de Mockito.verify(), voir ci-dessous.

Vérifier que la méthode a été appelée

Vérifier que la méthode a été appelée (à placer après l’appel de la méthode à tester) :

Mockito.verify(meteo).saveTemperature(2, 5);

Vérifier le nombre d’appels :

// Jamais appelée
Mockito.verify(meteo, Mockito.never()).saveTemperature(2, 5);

// Appelée une fois
Mockito.verify(meteo).saveTemperature(2, 5);

// Appelée trois fois
Mockito.verify(meteo, Mockito.times(3)).saveTemperature(2, 5);

// Appelée au moins une fois
Mockito.verify(meteo, Mockito.atLeastOnce()).saveTemperature(2, 5);

// Appelée au moins trois fois
Mockito.verify(meteo, Mockito.atLeast(3)).saveTemperature(2, 5);

// Appelée au plus six fois
Mockito.verify(meteo, Mockito.atMost(6)).saveTemperature(2, 5);

Condition sur les arguments

Par défaut, Mockito se base sur la méthode equals pour connaître les correspondances entre les arguments.

Dans certains cas, comme par exemple si on ne connait pas exactement les arguments passés en paramètre de la méthode, nous pouvons utiliser un Matchers.

Cependant, il faut respecter la règle suivante : Si un des arguments est un Matcher, alors tous les autres arguments doivent être également des Matcher.

Condition sur les arguments inconnus

// N'importe quel argument
Mockito.when(meteo.getTemperature(Matchers.anyInt())).thenReturn(10);
Mockito.verify(meteo).saveTemperature(Matchers.anyInt(), Matchers.anyInt());

Suivant le type de l’argument :

  • Matchers.any()
  • Matchers.anyString()
  • Matchers.anyInt()

Mélanger des conditions sur les arguments inconnus et connus

Il est nécessaire de toujours respecter cette règle lors de l’utilisation de Matchers : Si un des arguments est un Matcher, alors tous les autres arguments doivent être également des Matcher.

Dans le cas où nous voulons indiquer une valeur en particulier, nous pouvons utiliser Matcher.eq(value) de cette façon :

// Erreur : '2' n'est pas un Matcher
Mockito.verify(meteo).setTemp(2, Matchers.anyInt());

// OK
Mockito.verify(meteo).setTemp(Matchers.eq(2), Matchers.anyInt());

Condition spécifique sur les arguments

Pour avoir des conditions spécifiques sur les arguments, créer une classe qui étend ArgumentMatcher :

private class IsDateAfterMatcher extends ArgumentMatcher {
    @Override
    public boolean matches(Object argument) {
        Date d = (Date) argument;
        return d != null && d.after(new Date());
    }
}

, que nous pouvons utiliser dans les vérifications d’appel :

// valide que la date passée en argument de setDate est postérieure à la date du jour
Mockito.verify(obj).setDate(Mockito.argThat(new IsDateAfterMatcher()));

, ou pour retourner certaines valeurs en fonction de l’argument :

// retourne vrai si la date passée en argument de setDate est postérieure à la date du jour
Mockito.when(obj.isDateAfter(Mockito.argThat(new IsDateAfterMatcher()))).thenReturn(true);

Capturer un argument pour le vérifier après

A l’appel d’une méthode mockée, Mockito peut mémoriser l’argument qui a été passé en paramètre à l’aide d’un objet ArgumentCaptor qui va contenir l’argument.

Ceci est effectué à la fin du test, après l’appel de la méthode à tester.

Règle : Il faut mixer les ArgumentCaptor avec des Matchers, comme décrit plus haut.

// Après l'appel de la méthode de test, on définit l'objet de capture d'argument
final ArgumentCaptor arg = ArgumentCaptor.forClass(Integer.class);
// Capturer l'argument sur la température pour le jour 2
Mockito.verify(meteoDAO).saveTemperature(Matchers.eq(2), arg.capture());
// Vérifier que la température est de 9°C
Assert.assertEquals(Integer.valueOf(9), arg.getValue());

Vérifier l’ordre des appels

Créer l’objet InOrder en indiquant les classes mockées, puis effectuer les vérification dans l’ordre voulu à l’aide de la méthode verify en indiquant la classe mockée et la méthode appelée que celle-ci retourne une valeur ou non.

final InOrder inOrder = Mockito.inOrder(meteoPrediction, meteoDAO);
inOrder.verify(meteoPrediction).calculer(0);
inOrder.verify(meteoDAO).saveTemperature(0, 10);
inOrder.verify(meteoPrediction).calculer(1);
inOrder.verify(meteoDAO).saveTemperature(1, 8);

Mocker une partie des méthodes de la classe à tester

Pour mocker une partie des méthodes de la classe contenant la méthode à tester, nous définissons cette classe comme espionnée via Mockito.spy() et nous pouvons alors ajouter des comportements comme s’il s’agissait d’une classe mockée classique.

A la main :

ABC abc = Mockito.spy(new ABC());

Via les annotations :

@RunWith(MockitoJUnitRunner.class)
public class ABCServiceTest {

  @InjectMocks
  @Spy
  private ABCService abcService;
  @Mock
  private ABCCalcul abcCalcul;
  @Mock
  private ABCDAO abcDAO;

}

, avec :

  • @Mock : pour mocker la classe de la propriété qui suit cette annotation, équivalent à private ABC abc = Mockito.mock(ABC.class)
  • @InjectMocks : pour instancier la propriété qui suit cette annotation et lui injecter les classes qui ont été mockées via @Mock
  • @Spy : pour indiquer que la propriété est espionnée par Mockito, on peut mocker une partie des méthodes.

Définir qu’une méthode ne fait rien pour les classes espionnées

Pour indiquer que la méthode de la classe espionnée via Mockito.spy() n’effectue aucune action, il faut utiliser la syntaxe « Mockito.doNothing().when(mock) » et poser une vérification d’appel sur cette méthode :

// Espionné
MeteoDAO meteoDAO = Mockito.spy(new MeteoDAO());

Mockito.doNothing().when(meteoDAO.saveTemperature(2, 5));

// Vérifier l'appel
meteoDAO.saveTemperature(2, 5);
Mockito.verify(meteoDAO).saveTemperature(2, 5);

Définir les valeurs retournées pour les classes espionnées

Pour indiquer que la méthode de la classe espionnée via Mockito.spy() retourne une valeur, il faut utiliser la syntaxe « Mockito.doReturn(valeur).when(mock) » à la place de « Mockito.when(mock).thenReturn(valeur) » :

// Espionné
MeteoPrediction meteoPrediction = Mockito.spy(new MeteoPrediction());

// Retourne la valeur
Mockito.doReturn(9).when(meteoPrediction.getTemperature(2));

Tagged with: ,
Publié dans Mockito, Test, Tests unitaires

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l’aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s