Les erreurs courantes avec EasyMock

logo easymockEasyMock 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/

Articles similaires:

4 Responses to “Les erreurs courantes avec EasyMock”

  1. Thierry dit :

    Je te propose également un de mes vieux articles sur le sujet : http://thierry.leriche-dessirier.com/article/intro-easymock.htm

  2. Sandy dit :

    EasyMock is a good mocking framework but Mockito is far better, simpler and easier : http://code.google.com/p/mockito/wiki/MockitoVSEasyMock

  3. Stéphane dit :

    Je viens de me manger l’erreur 3 et de la résoudre en 30 secondes grâce à ton post, merci merci :)

  4. Ellène dit :

    Les erreurs EasyMock n’étant jamais très claires, ton billet devient pour moi une référence. Merci Mathilde :)

Leave a Reply