Les erreurs courantes avec EasyMock
EasyMock est un framework de test qui peut dérouter dans un premier abord. Une fois qu’on a compris comment l’utiliser, on tombe sur un certain nombre d’erreurs qui reviennent très souvent et qui ne sont pas souvent explicites. Même si EasyMock 3.0 a clarifié un certain nombre d’erreurs, cet article (fait sous la 2.5.2) servira de pense-bête à ceux qui débutent avec ce framework.
Les classes qui permettent de tester : une interface CalculService et son implémentation dont on cherche à tester unitairement les différentes méthodes. L’implémentation de CalculService fait appel à plusieurs méthodes de l’interface FormatService. Comme chacun des appels à la méthode FormatService est mocké, nous n’avons pas besoin de définir une implémentation correspondante.
package fr.java.freelance.service; import java.math.BigDecimal; public interface CalculService { String calcul(BigDecimal a); String calcul(BigDecimal a, BigDecimal b); String calcul(String year, String month, String day); }
package fr.java.freelance.service.impl; import java.math.BigDecimal; import fr.java.freelance.service.CalculService; import fr.java.freelance.service.FormatService; public class CalculServiceImpl implements CalculService { private final FormatService formatService; public CalculServiceImpl( FormatService formatService) { this.formatService = formatService; } public CalculServiceImpl() { formatService=null; } public String calcul(BigDecimal a, BigDecimal b) { return formatService.formatComplexe(a, b); } public String calcul(BigDecimal a) { return formatService.formatSimple(a); } public String calcul(String year, String month, String day) { return formatService.formatSimple(year, month, day); } }
package fr.java.freelance.service; import java.math.BigDecimal; public interface FormatService { String formatSimple(BigDecimal a); String formatComplexe(BigDecimal a,BigDecimal b); String formatSimple(String year, String month, String day); }
Erreur 1 :Unexpected method call methodX(X)
java.lang.AssertionError: Unexpected method call formatSimple(5): at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:43) at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:72) at $Proxy5.formatSimple(Unknown Source) @Test public void testUnexpectedMethodCall() { BigDecimal a = BigDecimal.valueOf(5); BigDecimal b = BigDecimal.valueOf(5); replay(formatServiceMock); calculService.calcul(a); verify(formatServiceMock); }
Explication : La méthode formatSimple est appelée avec le paramètre 5 sans qu’elle ait été attendue.
Erreur 2 :methodA(A,B): expected: X, actual: Y
java.lang.AssertionError: Expectation failure on verify: formatComplexe(5, 5): expected: 1, actual: 0 at org.easymock.internal.MocksControl.verify(MocksControl.java:111) at org.easymock.EasyMock.verify(EasyMock.java:1608) @Test public void testExpectedActual() { BigDecimal a = BigDecimal.valueOf(5); BigDecimal b = BigDecimal.valueOf(5); expect( formatServiceMock .formatSimple(eq(BigDecimal .valueOf(5)))).andReturn("XXX"); expect( formatServiceMock .formatComplexe(eq(BigDecimal .valueOf(5)),eq(BigDecimal .valueOf(5)))).andReturn("XXX"); replay(formatServiceMock); calculService.calcul(a); verify(formatServiceMock); }
Explication : La méthode formatComplexe(5, 5) était attendue une fois (expected:1) et n’a jamais été appelée (actual:0)
Erreur 3 : X matchers expected, Y recorded
java.lang.IllegalStateException: 2 matchers expected, 1 recorded. at org.easymock.internal.ExpectedInvocation.createMissingMatchers(ExpectedInvocation.java:56) @Test public void testExpectedRecorded() { BigDecimal a = BigDecimal.valueOf(5); BigDecimal b = BigDecimal.valueOf(6); expect( formatServiceMock .formatComplexe(BigDecimal .valueOf(5),eq(BigDecimal .valueOf(6)))).andReturn("XXX"); replay(formatServiceMock); calculService.calcul(a,b); verify(formatServiceMock); }
Il est interdit d’utiliser partiellement les comparateurs. Il faut les utiliser complètement ou pas du tout.
Ainsi
formatServiceMock.scale(BigDecimal .valueOf(5),eq(BigDecimal .valueOf(5)));
n’est pas OK (le deuxième argument utilise un comparateur, le premier la méthode equals => non OK).
formatServiceMock.scale(BigDecimal .valueOf(5),BigDecimal .valueOf(5));
OK
formatServiceMock.scale(eq(BigDecimal .valueOf(5)),eq(BigDecimal .valueOf(5)));
OK
Erreur 4 : Calling verify is not allowed in record state
java.lang.IllegalStateException: calling verify is not allowed in record state at org.easymock.internal.MocksControl.verify(MocksControl.java:109) at org.easymock.EasyMock.verify(EasyMock.java:1608) @Test public void testRecordState() { BigDecimal a = BigDecimal.valueOf(5); BigDecimal b = BigDecimal.valueOf(6); expect( formatServiceMock .formatComplexe(eq(BigDecimal .valueOf(5)),eq(BigDecimal .valueOf(6)))).andReturn("XXX"); calculService.calcul(a,b); verify(formatServiceMock); }
Explication : un simple oubli du replay
Erreur 5 :missing behavior definition for the preceding method call
java.lang.IllegalStateException: missing behavior definition for the preceding method call formatComplexe(5, 6) at org.easymock.internal.MocksControl.replay(MocksControl.java:101) at org.easymock.EasyMock.replay(EasyMock.java:1540) @Test public void testMissingBehavior() { BigDecimal a = BigDecimal.valueOf(5); BigDecimal b = BigDecimal.valueOf(6); formatServiceMock .formatComplexe(eq(BigDecimal .valueOf(5)),eq(BigDecimal .valueOf(6))); replay(formatServiceMock); calculService.calcul(a,b); verify(formatServiceMock); }
Explication : La méthode formatServiceMock.formatComplexe(BigDecimal a,BigDecimal b) renvoie un paramètre. Il faut donc indiquer quel paramètre de retour cette dernière doit renvoyée via la méthode expect et le andReturn. Dans ce cas, l’écriture correcte est :
expect( formatServiceMock .formatComplexe(eq(BigDecimal .valueOf(5)),eq(BigDecimal .valueOf(6)))).andReturn("XXX");
Erreur 6 : NullPointerException
@Test public void testNullPointer() { BigDecimal a = BigDecimal.valueOf(5); BigDecimal b = BigDecimal.valueOf(6); formatServiceMock .formatComplexe(eq(BigDecimal .valueOf(5)),eq(BigDecimal .valueOf(6))); replay(formatServiceMock); calculServiceDouble.calcul(a,b); verify(formatServiceMock); }
avec
@Before public void before() { formatServiceMock = createMock(FormatService.class); calculService = new CalculServiceImpl( formatServiceMock); calculServiceDouble = new CalculServiceImpl(); }
Explication : Dans 90% des cas, le mock n’a pas été affecté au service testé.
Le code source est dispo ici https://code.google.com/p/easymock-error/
Je te propose également un de mes vieux articles sur le sujet : http://thierry.leriche-dessirier.com/article/intro-easymock.htm
EasyMock is a good mocking framework but Mockito is far better, simpler and easier : http://code.google.com/p/mockito/wiki/MockitoVSEasyMock
Je viens de me manger l’erreur 3 et de la résoudre en 30 secondes grâce à ton post, merci merci
Les erreurs EasyMock n’étant jamais très claires, ton billet devient pour moi une référence. Merci Mathilde