Tests unitaires dans Liferay

Je viens de passer 2 ans en mission sur un projet basé sur le portail Liferay. Nous avons suivi les grands principes des méthodes agiles (Scrum notamment) et de ce fait réalisé un bon nombre de tests unitaires.  Afin de respecter LE grand principe des tests unitaires, à savoir ne pas du tout sortir de la méthode testée, nous avons dû notamment mocker (simuler) les appels static de Liferay (tel que UserLocalServiceUtil.method(), …). En effet, ces services utilisent généralement un bean spring qui va faire remonter notre appel dans les différentes couches de l’application, ce qui au final est tout sauf un appel unitaire …

Après quelques recherches sur le net, nous avons finalement découvert l’outil jMockIt dont voici un petit exemple très simple qui démontre comment simuler ces fameux appels static.

Tout d’abord, vous devez télécharger l’outil ici, puis ajouter le jar jmockit.jar à votre classpath.

Voici, dans un premier temps, la classe MyClass contenant une méthode que l’on souhaite tester :

 import com.liferay.portal.model.User; import com.liferay.portal.service.UserLocalServiceUtil;</code> public class MyClass { public String myTestMethod(long id) throws Exception { User user = UserLocalServiceUtil.getUser(id); if(user.getUserId()%2 == 0){ return "INACTIVE"; }else{ return "ACTIVE"; } } }

L’implémentation des objets du model Liferay n’est pas utilisable tel quel dans les tests unitaires. Il faut donc surcharger ces objets de la manière suivante :

 public class UserImplFake implements com.liferay.portal.model.User {</code> private long userId; private boolean active; //... A compléter selon vos besoins... @Override public int compareTo(User arg0) { return 0; } @Override public boolean getActive() { return active; } @Override public long getUserId() { return userId; } @Override public boolean isActive() { return active; } @Override public void setActive(boolean arg0) { active = arg0; } @Override public void setUserId(long arg0) { userId = arg0; } @Override public boolean getAgreedToTermsOfUse() { return false; } //... }

Tout est maintenant prêt pour écrire le test unitaire :

 import static org.junit.Assert.assertEquals; import mockit.Mock; import mockit.MockClass; import mockit.Mockit;</code> import org.junit.Test; import com.liferay.portal.PortalException; import com.liferay.portal.SystemException; import com.liferay.portal.model.User; import com.liferay.portal.service.UserLocalServiceUtil; public class ExampleTest { @Test public void test_MyTestMethod_idPair() throws Exception { Mockit.setUpMocks(MockUserLocalServiceUtil.class); MyClass test = new MyClass(); String myResult = test.myTestMethod(1234l); assertEquals("les utilisateurs d'id pair doivent être inactif","INACTIVE",myResult); } @Test public void test_MyTestMethod_idImpair() throws Exception { Mockit.setUpMocks(MockUserLocalServiceUtil.class); MyClass test = new MyClass(); String myResult = test.myTestMethod(1235l); assertEquals("les utilisateurs d'id impair doivent être actif","ACTIVE",myResult); } @MockClass(realClass = UserLocalServiceUtil.class) public static class MockUserLocalServiceUtil { @Mock public static User getUser(long userId) throws PortalException, SystemException { User user = new UserImplFake(); if(userId==1234){ user.setUserId(1234l); user.setActive(false); } if(userId==1235){ user.setUserId(1235l); user.setActive(true); } return user; } } }

Dans ce code, on demande à jMockit d’utiliser la classe MockUserLocalServiceUtil au lieu de la classe UserLocalServiceUtil uniquement pour la méthode getUser(long). Celle-ci se charge alors de renvoyer un objet utilisable dans un environnement clos.

Maintenant, essayez d’exécuter les tests unitaires.
L’erreur suivante doit alors apparaître :

<br></br>
java.lang.NoClassDefFoundError: Could not initialize class mockit.Mockit<br></br>
at com.ippon.blog.unittest.ExampleTest.test_MyTestMethod_idImpair(ExampleTest.java:32)

En effet, pour que tout fonctionne vous devez ajouter l’agent jmockit aux arguments de votre VM :
-javaagent:${project}\lib\jmockit.jar

Vos tests unitaires doivent maintenant passer au vert.

Sur notre projet nous avons également utilisé les classes offertes pas Spring dans le package org.springframework.mock.web.
Avec ces différents outils, les différentes couches de notre application ont pu être testées très facilement.

Vous pouvez trouver ici un article présentant un grand nombre des possibilités offertes par jmockit.