## EasyMock 2.3 Readme

Documentation de la version 2.3 (July 10 2007)
© 2001-2007 OFFIS, Tammo Freese.

Traduit originellement de l'anglais par Alexandre de Pellegrin. Maintenue par Henri Tremblay.

EasyMock 2 est une librairie fournissant un moyen simple d'utiliser des Mock Objects pour une interface donnée. EasyMock 2 est disponible sous licence MIT.

Les Mock Objects simulent le comportement du code métier et sont capables de vérifier s'il est utilisé comme prévu. Les classes métier peuvent être testées de façon isolée en simulant leurs objets liés par des Mock Objects.

Ecrire et maintenir des Mock Objects est souvent une tâche pénible et source d'erreurs. EasyMock 2 génère les Mock Objects dynamiquement - pas besoin de les écrire, pas de code généré!

### Avantages d'EasyMock 2

- Pas d'écriture manuelle des Mock Objects.
- Supporte le refactoring sur les Mock Objects : le code de test ne sera pas cassé au runtime lors du renommage de méthodes ou de la réorganisations de paramètres
- Supporte les valeurs de retour et les exceptions.
- Supporte la vérification de l'ordre d'appel des méthodes, sur un ou plusieurs Mock Objects.

### Inconvénients d'EasyMock 2

- EasyMock 2 fonctionne uniquement avec Java 2 Version 5.0 ou version supérieure.

Par défaut, EasyMock supporte la génération de Mock Objects uniquement pour les interfaces. Pour ceux souhaitant générer des Mock Objects pour des classes, il existe une extension disponible sur la page d'accueil d'EasyMock.

## Installation

1. Java 2 (minimum 5.0) est requis.
2. Décompressez le fichier zip d'EasyMock (`easymock2.3.zip`). Il contient le répertoire `easymock2.3`. Ajoutez le jar d'EasyMock (`easymock.jar`) de ce répertoire dans votre classpath.

Pour exécuter les tests d'EasyMock, ajoutez `tests.zip` et le jar de JUnit 4.1 à votre classpath and lancez `'java org.easymock.tests.AllTests'`.

Le code source d'EasyMock est situé dans `src.zip`.

## Utilisation

La plupart des éléments d'un logiciel ne fonctionnent pas de manière isolée mais en collaboration avec d'autres éléments (objets liés) pour effectuer leur tâche. Dans beaucoup de cas, nous ne nous soucions pas d'utiliser des objets liés pour nos tests unitaires du moment que nous avons confiance en eux. Si ce n'est pas le cas, les Mock Objects peuvent nous aider à tester unitairement de façon isolée. Les Mock Objects remplacent les objets liés de l'élément testé.

Les exemples suivants utilisent l'interface `Collaborator`:

    package org.easymock.samples;

    public interface Collaborator {
        void documentAdded(String title);
        void documentChanged(String title);
        void documentRemoved(String title);
        byte voteForRemoval(String title);
        byte[] voteForRemovals(String[] title);
    }

Les implémentations de cette interface sont des objets liés (des listeners dans ce cas) à la classe nommée `ClassUnderTest`:

    public class ClassUnderTest {
        // ...
        public void addListener(Collaborator listener) {
            // ...
        }
        public void addDocument(String title, byte[] document) {
            // ...
        }
        public boolean removeDocument(String title) {
            // ...
        }
        public boolean removeDocuments(String[] titles) {
            // ...
        }
    }

Le code de la classe et de l'interface est disponible dans le package `org.easymock.samples` dans `samples.zip`.

Les exemples qui suivent supposent que vous êtes familier avec le framework de test JUnit. Bien que les tests montrés ici utilisent JUnit version 3.8.1, vous pouvez également utiliser JUnit 4 ou TestNG.

### Votre premier Mock Object

Nous allons maintenant construire un cas de test et jouer avec pour comprendre les fonctionnalités du package EasyMock. Le fichier `samples.zip` contient une version modifiée de ce test. Notre premier test devra vérifier que la suppression d'un document non existant ne doit pas provoquer la notification de l'objet lié. Voici le test dans la définition du Mock Object:

    package org.easymock.samples;

    import junit.framework.TestCase;

    public class ExampleTest extends TestCase {

        private ClassUnderTest classUnderTest;
        private Collaborator mock;

        protected void setUp() {
            classUnderTest = new ClassUnderTest();
            classUnderTest.addListener(mock);
        }

        public void testRemoveNonExistingDocument() {
            // This call should not lead to any notification
            // of the Mock Object:
            classUnderTest.removeDocument("Does not exist");
        }
    }

Pour beaucoup de tests utilisant EasyMock 2, nous avons uniquement besoin de l'import statique des méthodes de la classe `org.easymock.EasyMock`. Cette classe est la seule non interne et non dépréciée d'EasyMock 2.

    import static org.easymock.EasyMock.*;
    import junit.framework.TestCase;

    public class ExampleTest extends TestCase {

        private ClassUnderTest classUnderTest;
        private Collaborator mock;

    }

Pour obtenir un Mock Object, il faut:

1. créer un Mock Object pour l'interface à simuler,
2. enregistrer le comportement attendu, puis
3. basculer le Mock Object à l'état 'replay'.

Voici le premier exemple:

        protected void setUp() {
            mock = createMock(Collaborator.class); // 1
            classUnderTest = new ClassUnderTest();
            classUnderTest.addListener(mock);
        }

        public void testRemoveNonExistingDocument() {
            // 2 (we do not expect anything)
            replay(mock); // 3
            classUnderTest.removeDocument("Does not exist");
        }

Après activation à l'étape 3, `mock` est un Mock Object de l'interface `Collaborator` qui n'attend aucun appel. Cela signifie que si nous changeons notre `ClassUnderTest` pour appeler n'importe quelle méthode de l'interface, le Mock Object lévera une `AssertionError`:

    java.lang.AssertionError:
      Unexpected method call documentRemoved("Does not exist"):
        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
        at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
        at $Proxy0.documentRemoved(Unknown Source)
        at org.easymock.samples.ClassUnderTest.notifyListenersDocumentRemoved(ClassUnderTest.java:74)
        at org.easymock.samples.ClassUnderTest.removeDocument(ClassUnderTest.java:33)
        at org.easymock.samples.ExampleTest.testRemoveNonExistingDocument(ExampleTest.java:24)
        ...

### Ajouter un comportement

Écrivons un second test. Si un document est ajouté à la classe testée, nous nous attendons à un appel à `mock.documentAdded()` sur le Mock Object avec le titre du document en argument:

        public void testAddDocument() {
            mock.documentAdded("New Document"); // 2
            replay(mock); // 3
            classUnderTest.addDocument("New Document", new byte[0]);
        }

Aussi, dans l'étape d'enregistrement (avant d'appeler `replay`), le Mock Object ne se comporte pas comme un Mock Object mais enregistre les appels de méthode. Après l'appel à `replay`, il se comporte comme un Mock Object, vérifiant que les appels de méthode attendus ont bien lieu.

Si `classUnderTest.addDocument("New Document", new byte[0])` appelle la méthode attendue avec un mauvais argument, le Mock Object lèvera une `AssertionError`:

    java.lang.AssertionError:
      Unexpected method call documentAdded("Wrong title"):
        documentAdded("New Document"): expected: 1, actual: 0
        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
        at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
        at $Proxy0.documentAdded(Unknown Source)
        at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:61)
        at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:28)
        at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)
        ...

Tous les appels attendus n'ayant pas eu lieu sont montrés, ainsi que tous les appels faits alors qu'ils étaient non attendus (aucun dans notre cas). Si l'appel à la méthode est effectué trop de fois, le Mock Object le signale également:

    java.lang.AssertionError:
      Unexpected method call documentAdded("New Document"):
        documentAdded("New Document"): expected: 1, actual: 1 (+1)
        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
        at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
        at $Proxy0.documentAdded(Unknown Source)
        at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:62)
        at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:29)
        at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)
        ...

### Vérifier le comportement

Il y a un type d'erreur dont nous ne nous sommes pas préoccupés jusqu'à présent: si nous décrivons un comportement, nous voulons vérifier qu'il est bien respecté. Le test qui suit passe si une méthode du Mock Object est appelée. Pour vérifier cela, nous devons appeler `verify(mock)`:

        public void testAddDocument() {
            mock.documentAdded("New Document"); // 2
            replay(mock); // 3
            classUnderTest.addDocument("New Document", new byte[0]);
            verify(mock);
        }

Si la méthode du Mock Object n'est pas appelée, l'exception suivante sera levée :

    java.lang.AssertionError:
      Expectation failure on verify:
        documentAdded("New Document"): expected: 1, actual: 0
        at org.easymock.internal.MocksControl.verify(MocksControl.java:70)
        at org.easymock.EasyMock.verify(EasyMock.java:536)
        at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:31)
        ...

Le message de l'exception liste tous les appels attendus qui n'ont pas eu lieu.

### Attendre un nombre explicite d'appels

Jusqu'à maintenant, nos tests ont été faits uniquement sur un seul appel de méthode. Le test suivant vérifiera que l'ajout d'un document déjà existant déclenche l'appel à `mock.documentChanged()` avec l'argument approprié. Pour en être certain, nous vérifions cela trois fois (après tout, c'est un exemple ;-)):

        public void testAddAndChangeDocument() {
            mock.documentAdded("Document");
            mock.documentChanged("Document");
            mock.documentChanged("Document");
            mock.documentChanged("Document");
            replay(mock);
            classUnderTest.addDocument("Document", new byte[0]);
            classUnderTest.addDocument("Document", new byte[0]);
            classUnderTest.addDocument("Document", new byte[0]);
            classUnderTest.addDocument("Document", new byte[0]);
            verify(mock);
        }

Afin d'éviter la répétition de `mock.documentChanged("Document")`, EasyMock fournit un raccourci. Nous pouvons spécifier le nombre d'appel avec la méthode `times(int times)` sur l'objet retourné par `expectLastCall()`. Le code ressemble alors à cela:

        public void testAddAndChangeDocument() {
            mock.documentAdded("Document");
            mock.documentChanged("Document");
            expectLastCall().times(3);
            replay(mock);
            classUnderTest.addDocument("Document", new byte[0]);
            classUnderTest.addDocument("Document", new byte[0]);
            classUnderTest.addDocument("Document", new byte[0]);
            classUnderTest.addDocument("Document", new byte[0]);
            verify(mock);
        }

Si la méthode est appelée un trop grand nombre de fois, une exception sera levée nous indiquant que la méthode a été appelée trop de fois. L'erreur est levée immédiatement après le premier appel dépassant la limite:

    java.lang.AssertionError:
      Unexpected method call documentChanged("Document"):
        documentChanged("Document"): expected: 3, actual: 3 (+1)
    	at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
    	at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
    	at $Proxy0.documentChanged(Unknown Source)
    	at org.easymock.samples.ClassUnderTest.notifyListenersDocumentChanged(ClassUnderTest.java:67)
    	at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:26)
    	at org.easymock.samples.ExampleTest.testAddAndChangeDocument(ExampleTest.java:43)
        ...

S'il y a trop peu d'appels, `verify(mock)` lève une `AssertionError`:

    java.lang.AssertionError:
      Expectation failure on verify:
        documentChanged("Document"): expected: 3, actual: 2
    	at org.easymock.internal.MocksControl.verify(MocksControl.java:70)
    	at org.easymock.EasyMock.verify(EasyMock.java:536)
    	at org.easymock.samples.ExampleTest.testAddAndChangeDocument(ExampleTest.java:43)
        ...

### Spécifier des valeurs de retour

Pour spécifier des valeurs de retour, nous encapsulons l'appel attendu dans `expect(T value)` et spécifions la valeur de retour avec la méthode `andReturn(Object returnValue)` sur l'objet retourné par `expect(T value)`.

Prenons par exemple la vérification du workflow lors de la suppresion d'un document. Si `ClassUnderTest` fait un appel pour supprimer un document, il doit demander aux objets liés de voter pour cette suppression par appel à `byte voteForRemoval(String title)`. Une réponse positive approuve la suppression. Si la somme de toutes les réponses est positive, alors le document est supprimé et l'appel à `documentRemoved(String title)` est effectué sur les objets liés:

        public void testVoteForRemoval() {
            mock.documentAdded("Document");   // expect document addition
            // expect to be asked to vote for document removal, and vote for it
            expect(mock.voteForRemoval("Document")).andReturn((byte) 42);
            mock.documentRemoved("Document"); // expect document removal
            replay(mock);
            classUnderTest.addDocument("Document", new byte[0]);
            assertTrue(classUnderTest.removeDocument("Document"));
            verify(mock);
        }

        public void testVoteAgainstRemoval() {
            mock.documentAdded("Document");   // expect document addition
            // expect to be asked to vote for document removal, and vote against it
            expect(mock.voteForRemoval("Document")).andReturn((byte) -42);
            replay(mock);
            classUnderTest.addDocument("Document", new byte[0]);
            assertFalse(classUnderTest.removeDocument("Document"));
            verify(mock);
        }

Le type de la valeur de retour est vérifié à la compilation. Par exemple, le code suivant ne compilera pas du fait que le type fourni ne correspond au type retourné par la méthode:

        expect(mock.voteForRemoval("Document")).andReturn("wrong type");

Au lieu d'appeler `expect(T value)` pour récuperer l'objet auquel affecter une valeur de retour, nous pouvons aussi utiliser l'objet retourné par `expectLastCall()`. Ainsi, au lieu de

        expect(mock.voteForRemoval("Document")).andReturn((byte) 42);

nous pouvons écrire

        mock.voteForRemoval("Document");
        expectLastCall().andReturn((byte) 42);

Ce type d'écriture doit uniquement être utilisé si la ligne est trop longue car il n'inclut pas la vérification du type à la compilation.

### Travailler avec les exceptions

Afin de spécifier les exceptions (plus précisemment: les Throwables) devant être levées, l'objet retourné par `expectLastCall()` et `expect(T value)` fournit la méthode `andThrow(Throwable throwable)`. Cette méthode doit être appelée durant l'étape d'enregistrement après l'appel au Mock Object pour lequel le `Throwable` doit être levé.

Les exception non "checkées" (comme `RuntimeException`, `Error` ainsi que toutes leurs sous classes) peuvent être levées de n'importe quelle méthode. Les exceptions "checkées" ne doivent être levées que pour methodes où cela est prévu.

### Créer des valeurs de retour ou des exceptions

Parfois, nous voulons que notre Mock Object retourne une valeur ou lève une exception créée au moment de l'appel. Depuis la version 2.2 d'EasyMock, l'objet retourné par `expectLastCall()` et `expect(T value)` fournit la méthode `andAnswer(IAnswer answer)` permettant de spécifier une implémentation de l'interface `IAnswer` utilisée pour créer une valeur de retour ou une exception.

Au sein d'`IAnswer`, les arguments passés lors de l'appel au mock sont disponibles via `EasyMock.getCurrentArguments()`. Si vous utilisez cela, les refactorings du type réorganisation de l'ordre des arguments brisseront vos tests. Vous êtes prévenu.

### Changer de comportement sur le même appel de méthode

Il est également possible de spécifier un changement de comportement pour une méthode. Les méthodes `times`, `andReturn` et `andThrow` peuvent être chaînées. Comme exemple, nous définissons `voteForRemoval("Document")` pour

- retourner 42 pour les trois premiers appels,
- lever une `RuntimeException` sur le quatrième appel,
- renvoyer -42 une fois.

        expect(mock.voteForRemoval("Document"))
            .andReturn((byte) 42).times(3)
            .andThrow(new RuntimeException(), 4)
            .andReturn((byte) -42);

### Être plus permissif sur le nombre d'appels

Afin d'être plus permissif sur le nombre d'appels attendus, des méthodes additionnelles peuvent être utilisées à la place de `times(int count):`

`times(int min, int max)`
  pour attendre entre `min` and `max` appels,
`atLeastOnce()`
  pour attendre au moins un appel, et
`anyTimes()`
  pour attendre une quantité non définie d'appels.

Si aucun nombre d'appels n'est explicitement défini, alors seul un appel est attendu. Pour le définir explicitement, vous pouvez utiliser `once()` ou `times(1)`.

### Mocks stricts

Sur un Mock Object retourné par `EasyMock.createMock()`, l'ordre d'appel des méthodes n'est pas vérifié. Si vous souhaitez avoir un Mock Object 'strict' vérifiant cet ordre, utilisez `EasyMock.createStrictMock()`.

Lorsqu'un appel inattendu à une méthode est fait sur un Mock Object 'strict', le message de l'exception contient les appels de méthode attendus à ce moment, suivi du premier appel en conflit. `verify(mock)` montre tous les appels de méthode manqués.

### Activer/Désactiver la vérification de l'ordre d'appel des méthodes

Il est parfois nécessaire qu'un Mock Object vérifie l'ordre d'appel sur certains appels uniquement. Pendant la phase d'enregistrement, vous pouvez activer la vérification de l'ordre d'appel en utilisant `checkOrder(mock, true)` et la désactiver en utilisant `checkOrder(mock, false)`.

Il y a deux différences entre un Mock Object 'strict' et un Mock Object 'normal':

1. Un mock 'strict' à la vérification de l'order d'appel activé à la cré.
2. Un mock 'strict' à la vérification de l'order d'appel activé ès reset (voir Réutilisation d'un Mock Object).

### Définir des comparateurs d'arguments pour plus de souplesse

Pour vérifier la correspondance à un appel de méthode prévu sur un Mock Object, les arguments`de type Object` sont comparés, par défaut, avec `equals()`. Cela peut introduire des problèmes. Considérons l'exemple suivant:

    String[] documents = new String[] { "Document 1", "Document 2" };
    expect(mock.voteForRemovals(documents)).andReturn(42);

Si la méthode est appelée avec un autre tableau ayant le même contenu, cela provoque une exception du fait que `equals()` compare l'identité des objets pour les tableaux:

    java.lang.AssertionError:
      Unexpected method call voteForRemovals([Ljava.lang.String;@9a029e):
        voteForRemovals([Ljava.lang.String;@2db19d): expected: 1, actual: 0
        documentRemoved("Document 1"): expected: 1, actual: 0
        documentRemoved("Document 2"): expected: 1, actual: 0
    	at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
    	at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
    	at $Proxy0.voteForRemovals(Unknown Source)
    	at org.easymock.samples.ClassUnderTest.listenersAllowRemovals(ClassUnderTest.java:88)
    	at org.easymock.samples.ClassUnderTest.removeDocuments(ClassUnderTest.java:48)
    	at org.easymock.samples.ExampleTest.testVoteForRemovals(ExampleTest.java:83)
        ...

Pour spécifier que seule l'égalité de tableau est nécessaire pour cet appel, utilisez la méthode `aryEq`, importée statiquement de la classe `EasyMock`:

    String[] documents = new String[] { "Document 1", "Document 2" };
    expect(mock.voteForRemovals(aryEq(documents))).andReturn(42);

Si vous souhaitez utiliser les comparateurs lors d'un appel, vous devez en utiliser pour chaque argument de la méthode appelée.

Voici quelques comparateurs prédéfinis disponible:

`eq(X value)`
  Vérifie que la valeur reçue égale la valeur attendue. Disponible pour tous les types primitifs et objets.
`anyBoolean()`, `anyByte()`, `anyChar()`, `anyDouble()`, `anyFloat()`, `anyInt()`, `anyLong()`, `anyObject()`, `anyShort()`
  Laisse passer n'importe quelle valeur. Disponible pour tous les types primitifs et objets.
`eq(X value, X delta)`
  Vérifie que la valeur recçue égale la valeur attendue, plus ou moins un delta. Disponible pour les `float` et `double`.
`aryEq(X value)`
  Vérifie que la valeur recçue égale la valeur attendue en s'appuyant sur `Arrays.equals()`. Disponible pour les tableaux d'objets et de types primitifs.
`isNull()`
  Vérifie que la valeur reçue est nulle. Disponible pour les objets.
`notNull()`
  Vérifie que la valeur reçue n'est pas nulle. Disponible pour les objets.
`same(X value)`
  Vérifie que la valeur reçue est la même que la value attendue. Disponible pour les objets.
`isA(Class clazz)`
  Vérifie que la valeur reçue est une instance de clazz ou d'une classe hérite ou implémente clazz. Disponible pour les objets.
`lt(X value)`, `leq(X value)`, `geq(X value)`, `gt(X value)`
  Vérifie que la valeur reçue est inférieure/inférieure ou égale/supérieure ou égale/supérieure à la valeur attendue. Disponible pour tous les types primitifs numériques et les implémentations de `Comparable`.
`startsWith(String prefix), contains(String substring), endsWith(String suffix)`
  Vérifie que la valeur reçue commence par/contient/se termine par la valeur attendue. Disponible pour les `String`s.
`matches(String regex), find(String regex)`
  Vérifie que la valeur reçue/une sous-chaîne de la valeur reçue correspond &à l'expression ré. Disponible pour les `String`s.
`and(X first, X second)`
  Est valide si les résultats des deux comparateurs utilisés en `first` et `second` sont vérifiés. Disponible pour tous les types primitifs et objets.
`or(X first, X second)`
  Est valide si l'un des résultats des deux comparateurs utilisés en `first` et `second` est vérifié. Disponible pour tous les types primitifs et objets.
`not(X value)`
  Est valide si le résultat du comparateur utilisé dans `value` est négatif.
`cmpEq(X value)`
  Vérifie que la valeur reçue égale la valeur attendue du point de vue de `Comparable.compareTo(X o)`. Disponible pour tous les types primitifs numériques et les implémentations de `Comparable`.
`cmp(X value, Comparator comparator, LogicalOperator operator)`
  Vérifie que `comparator.compare(reçue, value) operator 0` où `operator` est <,<=,>,>= ou ==.

### Définir son propre comparateur d'arguments

Il peut être intéressant de définir son propre comparateur d'argument. Prenons un comparateur dont le rôle serait de vérifier une exception par rapport à son type et message. Il pourrait être utilisé de la façon suivante:

        IllegalStateException e = new IllegalStateException("Operation not allowed.")
        expect(mock.logThrowable(eqException(e))).andReturn(true);

Deux étapes sont nécessaires pour réaliser cela: le nouveau comparateur doit être défini et la méthode statique `eqException` doit être déclarée.

Pour définir le nouveau comparateur d'argument, nous implémentons l'interface `org.easymock.IArgumentMatcher`. Cette interface contient deux méthodes: `matches(Object actual)`, vérifiant que l'argument reçu est bien celui attendu, et `appendTo(StringBuffer buffer)`, ajoutant au StringBuffer une chaine de caractères représentative du comparateur d'argument. L'implémentation est la suivante :

    import org.easymock.IArgumentMatcher;

    public class ThrowableEquals implements IArgumentMatcher {
        private Throwable expected;

        public ThrowableEquals(Throwable expected) {
            this.expected = expected;
        }

        public boolean matches(Object actual) {
            if (!(actual instanceof Throwable)) {
                return false;
            }
            String actualMessage = ((Throwable) actual).getMessage();
            return expected.getClass().equals(actual.getClass())
                    && expected.getMessage().equals(actualMessage);
        }

        public void appendTo(StringBuffer buffer) {
            buffer.append("eqException(");
            buffer.append(expected.getClass().getName());
            buffer.append(" with message \"");
            buffer.append(expected.getMessage());
            buffer.append("\"")");

        }
    }

La méthode `eqException` doit instancier le comparateur d'argument avec l'objet Throwable donné, le fournir à EasyMock via la méthode statique `reportMatcher(IArgumentMatcher matcher)` et retourner une valeur afin d'être utilisée au sein de l'appel à la méthode mockée (typiquement `0`, `null` ou `false`). Une première tentative ressemblerait à ceci:

    public static Throwable eqException(Throwable in) {
        EasyMock.reportMatcher(new ThrowableEquals(in));
        return null;
    }

Cependant, cela ne fonctionnerait que si la méthode `logThrowable` de l'exemple acceptait `Throwable`s et quelque chose de plus spécifique du style de `RuntimeException`. Dans ce dernier cas, le code de notre exemple ne compilerait pas:

        IllegalStateException e = new IllegalStateException("Operation not allowed.")
        expect(mock.logThrowable(eqException(e))).andReturn(true);

Java 5.0 à la rescousse: Au lieu de définir `eqException` avec un `Throwable` en paramètre, nous utilisons un type générique qui hérite de `Throwable`:

    public static <T extends Throwable> T eqException(T in) {
        reportMatcher(new ThrowableEquals(in));
        return null;
    }

### Réutilisation d'un Mock Object

Les Mock Objects peuvent être réinitialisés avec `reset(mock)`.

### Utilisation d'un comportement de "stub" pour les méthodes

Dans certains cas, nous voudrions que nos Mock Object répondent à certains appels, mais sans tenir compte du nombre de fois, de l'ordre ni même s'ils ont été eu lieu. Ce comportement de "stub" peut être défini en utilisant les méthodes `andStubReturn(Object value)`, `andStubThrow(Throwable throwable)`, `andStubAnswer(IAnswer answer)` et `asStub()`. Le code suivant configure le Mock Object pour répondre 42 à `voteForRemoval("Document")` une fois et -1 pour tous les autres arguments:

        expect(mock.voteForRemoval("Document")).andReturn(42);
        expect(mock.voteForRemoval(not(eq("Document")))).andStubReturn(-1);

### Création de mocks dits "gentils"

Pour un Mock Object retourné par `createMock()`, le comportement par défaut pour toutes les méthodes est de léver une `AssertionError` pour tous les appels non prévus. Si vous souhaitez avoir un Mock Object "gentil" autorisant, par défaut, l'appel à toutes les méthodes et retournant la valeur vide appropriée (`0`, `null` ou `false`), utilisez `createNiceMock()` au lieu de `createMock()`.

### Méthodes de la classe Object

Les comportements des trois méthodes `equals()`, `hashCode()` et `toString()` ne peuvent être changés sur des Mock Objects créés avec EasyMock, même si elles font partie de l'interface duquel le Mock Object est créé.

### Vérifier l'ordre d'appel des méthodes entre plusieurs Mocks

Jusqu'à présent, nous avons vu un Mock Object comme étant seul et configuré par les méthodes statiques de la classe `EasyMock`. Mais beaucoup de ces méthodes statiques font référence à l'objet "control" caché de chaque Mock Object et lui délègue l'appel. Un Mock Control est un objet implementant l'interface `IMocksControl`.

Du coup, au lieu de

        IMyInterface mock = createStrictMock(IMyInterface.class);
        replay(mock);
        verify(mock);
        reset(mock);

nous pourrions utiliser le code équivalent:

        IMocksControl ctrl = createStrictControl();
        IMyInterface mock = ctrl.createMock(IMyInterface.class);
        ctrl.replay();
        ctrl.verify();
        ctrl.reset();

L'interface `IMocksControl` permet de créer plus d'un seul Mock Object. Ainsi, il est possible de vérifier l'ordre d'appel des méthodes entre les mocks. Par exemple, configurons deux mock objects pour l'interface `IMyInterface` pour lesquels nous attendons respectivement les appels à `mock1.a()` et `mock2.a()`, un nombre indéfini d'appels à `mock1.c()` et `mock2.c()`, et enfin `mock2.b()` et `mock1.b()`, dans cet ordre:

        IMocksControl ctrl = createStrictControl();
        IMyInterface mock1 = ctrl.createMock(IMyInterface.class);
        IMyInterface mock2 = ctrl.createMock(IMyInterface.class);

        mock1.a();
        mock2.a();

        ctrl.checkOrder(false);

        mock1.c();
        expectLastCall().anyTimes();
        mock2.c();
        expectLastCall().anyTimes();

        ctrl.checkOrder(true);

        mock2.b();
        mock1.b();

        ctrl.replay();

### Nommer un Mock Object

Les Mock Objects peuvent ê nommés à leur création en utilisant `createMock(String name, Class toMock)`, `createStrictMock(String name, Class toMock)` ou `createNiceMock(String name, Class toMock)`. Les noms seront affichés dans le message des `AssertionError`.

### Compatibilité avec les anciennes versions

EasyMock 2 contient une couche de compatibilité. Ainsi, les tests utilisant EasyMock 1.2 en Java 1.5 fonctionneront sans aucune modification. Les seules différences connues sont visibles lorsque qu'un test est en erreur: il y a de légers changements dans les messages d'erreur, les stack traces et les erreurs sont maintenant remontées en utilisant `AssertionError` de Java au lieu de `AssertionFailedError` de JUnit.

EasyMock 2.1 introduisait une fonctionnalité de callback qui a été retirée dans EasyMock 2.2, car trop complexe. Depuis EasyMock 2.2, l'interface `IAnswer` fournit la fonctionnalité de callback.

## Développement d'EasyMock

EasyMock 1.0 a été développé par Tammo Freese chef OFFIS. Le développement d'EasyMock est hébergé par SourceForge pour permettre à d'autres développeurs et sociétés d'y contribuer.

Rmerciements à ceux qui nous ont fourni retour d'expérience et rustines, incluant Nascif Abousalh-Neto, Dave Astels, Francois Beausoleil, George Dinwiddie, Shane Duan, Wolfgang Frech, Steve Freeman, Oren Gross, John D. Heintz, Dale King, Brian Knorr, Dierk Koenig, Chris Kreussling, Robert Leftwich, Patrick Lightbody, Johannes Link, Rex Madden, David McIntosh, Karsten Menne, Bill Michell, Stephan Mikaty, Ivan Moore, Ilja Preuss, Justin Sampson, Markus Schmidlin, Richard Scott, Joel Shellman, Jiří Mareš, Alexandre de Pellegrin Shaun Smith, Marco Struck, Ralf Stuckert, Victor Szathmary, Henri Tremblay, Bill Uetrecht, Frank Westphal, Chad Woolley, Bernd Worsch, et de nombreux autres.

Merci de consulter la page d'accueil EasyMock pour être informé des nouvelles versions et transmettez vos bogues et suggestions à EasyMock Yahoo!Group (en anglais SVP). Si vous souhaitez souscrire au EasyMock Yahoo!Group, envoyez un message à easymock-subscribe@yahoogroups.com.

EasyMock Version 2.3 (July 10 2007)

Changements depuis la version 2.2:

- Documentation en français
- Comparateurs pour arguments de type Comparable
- Correction de la comparaison de nombres décimaux
- Les Mock Objects peuvent maintenant être nommés
- Includ le fix de Bill Michell pour sur les ThreadLocals
- Migration des test unitaires d'EasyMock vers JUnit 4

Changements depuis la version 2.1:

- la réponse aux appels peut ètre créée au moment de l'appel via `andAnswer(IAnswer answer)` et `andStubAnswer(IAnswer answer)`
- `callback(Runnable runnable)` a été retiré, pour un callback, veuillez convertir à `andAnswer(IAnswer answer)` et `andStubAnswer(IAnswer answer)`
- `replay()`, `verify()` et `reset()` acceptent maintenant plusieurs mock objects comme arguments

Changements depuis la version 2.0:

- les arguments passés à un mock object sont à disponibles dans un callback via `EasyMock.getCurrentArguments()`
- Correction du bogue reporté par http://groups.yahoo.com/group/easymock/message/558
- remontée plus rapide d'une erreur si des comparateurs ne sont pas utilisés

Changements depuis la version 1.2:

- support de comparateurs d'arguments souple et ré au refactoring-safe
- aucun mock control n'est nécessaire pour un Mock Object solitaire
- le comportement de "stub" remplace le comportement "défaut"
- support de la vérification d'ordre d'appels entre plusieurs mocks avec possibilité d'activer/désactiver cette vérification
- support de callbacks
- EasyMock lève désormais des `java.lang.AssertionError` au lieu de `junit.framework.AssertionFailedError` ce qui permet d'être indépendant du framework de test. Vous pouvez l'utiliser avec JUnit 3.8.x, JUnit 4 et TestNG
- ancienne API dépréciée