PowerMock Mock Mock

PowerMock c’est quoi ?

PowerMock est une extension de 2 API bien connues des mockeurs (Ha ha), EasyMock et Mockito. Qui n’as jamais été obligé de supprimer un final, rendre une méthode private protected ou galérer sur des appels statiques pour faire ses tests ? PowerMock nous permet de « tester l’intestable » sans toucher au code. Bonne idée ? Oui, mais attention, cette bonne idée a un coût qu’il ne faut pas négliger. L’exécution des tests peut prendre jusqu’à 10 fois plus de temps.

De plus, si on ne fait pas attention, on peut tomber sur des anomalies qui peuvent être difficile à résoudre si on n’a pas saisi le « truc ». L’objet de cet article est de vous présenter ce qui me crash le plus souvent mes tests.


Le problème

Voici la classe à tester :

package fr.java.freelance;

public class UnTrucCool {

public final boolean estQuelqueChose(){
return critereUn() && critereDeux();
}

public boolean critereUn(){
return false;
}

public boolean critereDeux(){
return false;
}

}

En fait, un test existe déjà :

package fr.java.freelance;

import static org.junit.Assert.*;

import org.junit.Test;
import static org.mockito.Mockito.*;

public class UnTrucCoolTest {

@Test
public void testEstQuelqueChose() {
UnTrucCool banane = mock(UnTrucCool.class);
when(banane.critereDeux()).thenReturn(true);
when(banane.critereUn()).thenReturn(true);

assertTrue(banane.estQuelqueChose());
}

}

Pour une bonne raison, nous décidons d’utiliser PowerMock, nous modifions donc le test comme ceci :

package fr.java.freelance;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.powermock.api.mockito.PowerMockito.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest(UnTrucCool.class)
public class UnTrucCoolPMTest {

@Test
public void testEstQuelqueChose() {
UnTrucCool banane = mock(UnTrucCool.class);
when(banane.critereDeux()).thenReturn(true);
when(banane.critereUn()).thenReturn(true);

assertTrue(banane.estQuelqueChose());
}

}

Le test est strictement identique, mais ça ne fonctionne plus! estQuelqueChose() renvoie systématiquement false !


La solution

Dans le premier test nous utilisons simplement Mockito qui ne sait pas mocker les méthodes finales. Comme il ne sait pas le faire, il adopte un comportement par défaut et appel la méthode réelle.  En passant à PowerMock, toutes les méthodes finales peuvent être mockées et doivent donc avoir un comportement défini explicitement. On notera que si estQuelquechose() n’avait pas été finale, le test Mockito aurait également dû définir explicitement le comportement à avoir.

@RunWith(PowerMockRunner.class)
@PrepareForTest(UnTrucCool.class)
public class UnTrucCoolPMTest {

 @Test
 public void testEstQuelqueChose() {
 UnTrucCool banane = mock(UnTrucCool.class);
 when(banane.critereDeux()).thenReturn(true);
 when(banane.critereUn()).thenReturn(true);

 when(banane.estQuelqueChose()).thenCallRealMethod();

 assertTrue(banane.estQuelqueChose());
 }

}

Faites attention lorsque vous passez un existant sur PowerMock.

http://code.google.com/p/powermock/

4 Responses to “PowerMock Mock Mock”

  1. Quelle est la « bonne raison » qui t’a fait passer à PowerMock? Le besoin de mocker des méthodes static ou final?

  2. Jean-Baptiste dit :

    Oui la problèmatique sur nos applicatifs c’est qu’ils ont plus de 10 ans. Avec tout ce que cela suppose (architecture bancale, dettes technique forte, peu de tests unitaires). On est souvent obligé de mocker des méthodes statiques ou private.

    Ceci dit on peut avoir bien d’autre raison d’utiliser PowerMock, par exemple si on utilise une API externe qu’on ne peut pas modifier et qui définie ses classes « final ».

  3. Benoît Dissert dit :

    Je pense que c’est une raison valable, surtout si on sait que Joshua Bloch recommande de toujours déclarer les méthodes finales, sauf si elles ont une bonne raison de pas l’être.

  4. Jean-Baptiste dit :

    Oui cela va même plus loin. Idéalement il ne faudrait que des classes concrètes « final » et des classes abstraites avec toutes ses méthodes publiques « final » également, le tout immutable ! Mais bon, il faut aussi savoir rester pragmatique 😉 Je ne suis pas sûr que cela vaille le coup de rendre les classes difficilement testables et d’augmenter la durée des tests juste pour blinder une API.

Leave a Reply