Il existe certaines situations dans lesquelles on doit tester un composant sans pouvoir utiliser les composants dont il dépend. C'est notamment le cas quand on dépend d'un composant qui n'est lui-même pas encore développé (ce qui est assez fréquent dans les projets menés selon des méthodes agiles).
Pour pouvoir tout de même mener à bien les tests, nous allons devoir simuler ces composants ou leur comportement, grâce à des ruses techniques…
On utilise pour cela des simulacres (mock objects) ou des bouchons (stubs). En théorie, la différence entre un mock et un stub n'est pas facile à cerner. En pratique, c'est beaucoup plus simple : prenons un exemple !
La figure ci-dessus présente une classe métier Personne
dont pourrait bien dépendre d'autres classes que nous souhaiterions tester. Par exemple, dans un système de gestion des ressources humaines, une classe Équipe
agrègerait des instances de la classe Personne
. Dans ce cas, comment procéder si l'on a besoin de tester la classe Équipe alors que la classe Personne
n'est pas encore développée ?
La figure présente deux ruses techniques qu'il est possible de mettre en œuvre dans le code de test :
Personne
), et qui est une version simplifiée de la classe à tester, programmée pour l'occasion. On utilisera des instances de cette classe en lieu et place des instance de la vraie classe Personne
.Personne
d'exécuter sa méthode direBonjour()
, alors elle n'aura qu'à afficher "Coucou !" plutôt que d'exécuter le code de la vraie méthode direBonjour()
programmée dans la classe Personne
».Pour illustrer l'utilisation mocks, nous allons à nouveau nous appuyer sur un exemple : imaginions que nous développions un client de messagerie très simple en Java, et que nous ayons à tester un module permettant l'affichage des messages. Voici un exemple d'un tel affichage :
=====================
Vos nouveaux messages
=====================
Bonjour !, reçu le Wed Dec 07 00:00:00 CET 3910, de : bob@u-picardie.fr
Comment ça va ?
Un test, reçu le Thu Dec 08 00:00:00 CET 3910, de : test@u-picardie.fr
Bonjour le test !
=====================
Réellement basique comme client de messagerie… mais cela suffira pour illustrer notre propos !
Message
Message.java
package com.parser;
import java.util.Date;
public class Message {
Date date;
String subject;
String exp;
String content;
public Message(Date date, String subject, String exp, String content) {
this.date = date;
this.subject = subject;
this.exp = exp;
this.content = content;
}
public String toString() {
return subject + ", reçu le " + date + ", de : " + exp + "\n" + content;
}
}
Client
(de messagerie)Client.java
package com.parser;
import java.util.List;
public class Client {
ServerStuff serverStuff;
public Client(ServerStuff serverStuff) {
this.serverStuff = serverStuff;
}
public String buildMessagesOutput() {
List messages = this.serverStuff.getMessages();
String output = "";
output += "=====================\n";
output += "Vos nouveaux messages\n";
output += "=====================\n";
for (Message message : messages) {
output += message + "\n";
}
output += "=====================";
return output;
}
}
Manque de bol, la brique de connexion au serveur et de récupération des nouveaux messages (la classe ServerStuff
) n'a pas encore été développée. Tout ce que nous savons, c'est que d'après le design de l'application, ce composant est censé permettre la récupération d'une liste de messages List<Message>
.
Pour tester notre module d'affichage, nous allons donc mettre en place un mock de la classe ServerStuff
.
L'exemple Java présenté ci-dessous se base sur le framework de test Java jUnit, complété de la librairie Mockito.
Client.java
package com.parser;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import junit.framework.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ClientTest extends TestCase {
private Client client;
public void setUp() {
// La classe Client a besoin de ServerStuff
// qui n'est pas encore développée...
// Créons un mock :
ServerStuff serverStuff = mock(ServerStuff.class);
when(serverStuff.getMessages()).thenReturn((
List)Arrays.asList(
new Message(new Date(2010, 11, 7), "Bonjour !", "bob@u-picardie.fr", "Comment ça va ?"),
new Message(new Date(2010, 11, 8), "Un test", "testeur@u-picardie.fr", "Bonjour le test !")
)
);
// Nous pouvons à présent instancier le client :
this.client = new Client(serverStuff);
}
public void testDisplayMessages() {
String output = this.client.buildMessagesOutput();
assertTrue(output.equals("=====================\nVos nouveaux messages\n=====================\nBonjour !, reçu le Wed Dec 07 00:00:00 CET 3910, de : bob@u-picardie.fr\nComment ça va ?\nUn test, reçu le Thu Dec 08 00:00:00 CET 3910, de : testeur@u-picardie.fr\nBonjour le test !\n====================="));
}
}
Comme vous pouvez le constater, la mise en œuvre d'un mock est réellement simple. Il s'agit simplement d'une déclaration du type :
Soit monMock un mock de la classe MaClasse.
Quand quelqu'un appelle la méthode maMéthode de monMock, alors il faudra retourner ceci : [ce que l'on veut retourner].