Mocking en Java
Explication
Exemple d'utilisation du framework Mockito
Mockito est un projet open-source supporté par Google. Sa syntaxe est une des plus simples que j'ai pu observer parmi les différents frameworks de Mock Java. Pour l'utiliser, rendez-vous simplement sur le site web http://mockito.org/, télécharger le jar et importer-le dans le build path de votre projet.
Nous allons maintenant passer à un exemple d'application de Mocking. Voici pour commencer une interface IAccount :
package fr.uml.mocking.demo;
public interface IAccount {
boolean passwordMatches(String pass);
void setLoggedIn(boolean b);
}
Cette interface est utilisée par la classe "LoginService" qui permet l'authentification d'un Account.
package fr.uml.mocking.demo;
public class LoginService {
private final IAccount account;
public LoginService(IAccount account) {
this.account = account;
}
public void login(String pass) {
if (account.passwordMatches(pass)) {
account.setLoggedIn(true);
}
}
}
Nous souhaitons maintenant passer à la partie la plus intéressante, les tests unitaires. Nous souhaitons donc réaliser un test unitaire sur la méthode "login" de la classe LoginService.
Mais le problème suivant se pose : la classe IAccount n'a pas encore été implémentée et son développement a été confié à un autre développeur qui est pour l'instant occupé. Cela nous empêche de faire nos tests unitaires et vérifier que notre code fonctionne. Le mock va répondre à notre problématique, nous allons créer un objet mock qui va se substituer à la classe Account, démonstration :
package fr.uml.mocking.demo;
import org.junit.Test;
import static org.mockito.Mockito.*;
public class LoginServiceTest {
@Test
public void setLoginAccount() {
//given
IAccount account = mock(IAccount.class);
when(account.passwordMatches(anyString())).thenReturn(true);
//when
LoginService service = new LoginService(account);
service.login("pass");
//then
verify(account).setLoggedIn(true);
}
}
Ce code fonctionne et compile. Il nous assure que la méthode "login" de la classe LoginService fonctionne comme attendu. Vous pouvez apprécier la syntaxe très simple de Mockito. Nous créons un objet Mock de type IAccount puis nous lui affectons un comportement : lorsque la méthode "passwordMatches" est appelée avec n'importe quelle chaîne, le mock renverra la valeur True.
Le framework a crée dynamiquement un objet de type IAccount qui possède des comportements. Ces comportements sont de plus paramétrable à souhait, vous pouvez à peu près réaliser toutes les possibilités de comportement (pour en savoir plus, consulter la documentation de Mockito).
Comment Mock fonctionne ?
Vous vous posez maintenant la question "comment est-ce que le framework affecte à un objet virtuel des comportements ?"
Pour répondre à cette question, je me suis donc intéressé au fonctionnement interne des frameworks. Il existe donc deux stratégies pour créer un framework de Mocking en Java.
- Basé la classe java.lang.reflect.Proxy (Mockito, Easymock, JMock)
- On crée un proxy qui sera utilisé à la place d’un autre objet
- Basé sur la classe java.lang.Instrument (Jmockit)
- Un classloader spécifique est utilisé pour dynamiquement charger une classe à la place d’une autre
Passons au chapitre suivant dans lequel je vous explique la première stratégie d'implémentation.