Tester ses applications Liferay avec Arquillian

1. Pré-requis

Logiciels utilisés :

  • OS (Windows, Mac OSX ou Linux)
  • jdk 7 dernière version
  • MySQL dernière version
  • liferay-portal-tomcat-6.2-ce-ga4-20150416163831865.zip
  • apache-maven-3.2.3-bin.tar.gz
  • liferay-developer-studio-win-x64-2.2.2-ga3-201504240205.zip

Dans le document, on fera référence aux valeurs suivantes :

  • LIFERAY_HOME : répertoire d’installation de Liferay
  • TOMCAT_HOME : répertoire d’installation de Tomcat utilisé par Liferay

Vous deviez avoir un environnement de développement prêt avec un Liferay qui tourne sur une base de donnée MySQL.

2.Introduction

Faire des tests unitaires ou d’intégration sur le code développé sous Liferay est souvent fastidieux. En effet, sur les 6 plugins proposés par Liferay, il est possible de faire des tests unitaires ou d’intégrations sur seulement 4 d’entre elles (Portlet, Hook, Web et EXT). Les plugins thème et layout template ne contiennent uniquement de l’affichage.

Il est possible de faire les tests unitaires/d’intégration en mockant les services Liferay. Mais cela ne s’applique pas réellement sur le plugin Hook. En effet, faire un test unitaire/d’intégration sur une surchage d’un service Liferay par exemple n’est pas le reflet réelle du comportement de Liferay.

Dans cet article, nous allons voir l’utilisation du framework Arquillian qui permet de faire les tests unitaires/d’intégration sans mock. Le principe est que le plugin est packagé et déployé sur le serveur Liferay le temps du test et est immédiatement supprimé après les tests.

Ici, on se focalisera sur les plugins Porlet et Hook. Il est préconisé de ne jamais utiliser de plugin EXT. Le plugin Web est très peu utilisé et peut utiliser les frameworks de base de tests.

3. Installation et configuration

Avant de débuter à faire les tests unitaires/d’intégration, il est nécessaire d’ajuster quelques configurations afin que les tests fonctionnent.

3.1. Connexion avec Tomcat

Afin qu’Arquillian puisse fonctionner dans Tomcat, il est nécessaire de donner des droits à Tomcat afin qu’Arquillian puisse déployer l’application à tester.

  • Aller dans TOMCAT_HOME/conf
  • Ouvrir le fichier « tomcat-users.xml »
  • Ajouter le code suivant entre les balises « tomcat-users »
<role rolename="tomcat" />
<role rolename="manager-gui" />
<role rolename="manager-script" />
<role rolename="manager-jmx" />
<role rolename="manager-status" />
<user username="tomcat" password="tomcat" roles="tomcat,manager-gui,manager-script,manager-jmx,manager-status" />
  • Sauvegarder

3.2. Connexion pour Arquillian

Comme Arquillian va utiliser le manager de Tomcat pour déployer les applications dans le cadre de l’exécution des tests, il faut modifier les propriétés de l’environnement

  • Aller dans TOMCAT_HOME/bin
  • Ouvrir le fichier « setenv.sh » (setenv.bat pour Windows)
  • Ajouter le code suivant à la fin du fichier
JMX_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=8099 -Dcom.sun.management.jmxremote.ssl=false"

CATALINA_OPTS="$CATALINA_OPTS $JMX_OPTS"

PS : on utilise le port par défaut de JMX qui est 8099. Il est possible de le changer si besoin.

3.3. Configuration Maven

A aujourd’hui les dépendances d’Arquillian pour Liferay ne sont pas disponibles sur le repository central de Maven. Cependant ils sont disponibles sur le repository de Liferay.

  • Ouvrir le fichier « settings.xml » de Maven (localisé dans %username%/.m2). Si ce fichier n’existe pas, il faut le créer et récupérer la configuration de base de Maven.
  • Ajouter le code suivant entre les balises « mirrors »
<mirror>
    <id>artifactory</id>
    <mirrorOf>*</mirrorOf>
    <url>http://repository.liferay.com/nexus/content/groups/public</url>
    <name>Liferay</name>
</mirror>
  • Sauvegarder
  • Ajouter un profile avec le chemin vers Liferay
<profile>
	<id>liferay-6.2</id>
	<properties>
	<liferay.home>%LIFERAY_HOME%</liferay.home>
	</properties>
</profile>

3.4. Configuration des projets

Tous les projets doivent contenir les dépendances vers Arquillian.

  • Ajouter dans le fichier « pom.xml »
<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.jboss.arquillian</groupId>
			<artifactId>arquillian-bom</artifactId>
			<version>1.1.8.Final</version>
			<scope>import</scope>
			<type>pom</type>
		</dependency>
	</dependencies>
</dependencyManagement>
  • Ajouter ensuite les dépendances
<dependency>
	<groupId>com.liferay.arquillian</groupId>
	<artifactId>arquillian-liferay-maven-extension</artifactId>
	<version>1.0.0.Alpha1-SNAPSHOT</version>
</dependency>
<dependency>
	<groupId>org.jboss.arquillian.junit</groupId>
	<artifactId>arquillian-junit-container</artifactId>
	<version>1.1.8.Final</version>
</dependency>
<dependency>
	<groupId>org.jboss.arquillian.container</groupId>
	<artifactId>arquillian-tomcat-remote-7</artifactId>
	<version>1.0.0.CR7</version>
</dependency>
  • Chaque test doit contenir dans son classpath un fichier nommé « arquillian.xml » qui permet de faire la connexion avec Tomcat et qui contient :
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://jboss.org/schema/arquillian" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
	<container qualifier="tomcat" default="true">
		<configuration>
			<property name="jmxPort">8099</property>
			<property name="host">localhost</property>
			<property name="httpPort">8080</property>
			<property name="user">tomcat</property>
			<property name="pass">tomcat</property>
		</configuration>
	</container>
</arquillian>

Editer les valeurs si elles sont différentes.

4. Tests des plugins Liferay

4.1. Prélude

Créons un projet afin de tester le framework Arquillian dans Liferay. Celui-ci contriendra un pom parent avec 2 modules corresspondants au plugin Portlet et au plugin Hook.

  • Créer un dossier nommé « arquillian »
  • Créer un fichier « pom.xml » qui sera le pom parent du projet dans ce dossier avec
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>fr.sedona.arquillian</groupId>
	<artifactId>arquillian</artifactId>
	<packaging>pom</packaging>
	<name>[Arquillian] Parent</name>
	<version>1.0-SNAPSHOT</version>

	<properties>
		<liferay.auto.deploy.dir>${liferay.home}/deploy</liferay.auto.deploy.dir>
		<liferay.app.server.deploy.dir>${liferay.home}/tomcat-7.0.42/webapps</liferay.app.server.deploy.dir>
		<liferay.app.server.lib.global.dir>${liferay.home}/tomcat-7.0.42/lib/ext</liferay.app.server.lib.global.dir>
		<liferay.app.server.portal.dir>${liferay.home}/tomcat-7.0.42/webapps/ROOT</liferay.app.server.portal.dir>
		<liferay.version>6.2.3</liferay.version>
		<liferay.maven.plugin.version>6.2.10.12</liferay.maven.plugin.version>
	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>com.liferay.maven.plugins</groupId>
				<artifactId>liferay-maven-plugin</artifactId>
				<version>${liferay.maven.plugin.version}</version>
				<configuration>
					<autoDeployDir>${liferay.auto.deploy.dir}</autoDeployDir>
					<appServerDeployDir>${liferay.app.server.deploy.dir}</appServerDeployDir>
					<appServerLibGlobalDir>${liferay.app.server.lib.global.dir}</appServerLibGlobalDir>
					<appServerPortalDir>${liferay.app.server.portal.dir}</appServerPortalDir>
					<liferayVersion>${liferay.version}</liferayVersion>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.5</version>
				<configuration>
					<encoding>UTF-8</encoding>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-resources-plugin</artifactId>
				<version>2.5</version>
				<configuration>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.jboss.arquillian</groupId>
				<artifactId>arquillian-bom</artifactId>
				<version>1.1.8.Final</version>
				<scope>import</scope>
				<type>pom</type>
			</dependency>

			<dependency>
				<groupId>junit</groupId>
				<artifactId>junit</artifactId>
				<version>4.12</version>
				<scope>test</scope>
			</dependency>
			<dependency>
				<groupId>com.liferay.arquillian</groupId>
				<artifactId>arquillian-liferay-maven-extension</artifactId>
				<version>1.0.0.Alpha1-SNAPSHOT</version>
				<scope>test</scope>
			</dependency>
			<dependency>
				<groupId>org.jboss.arquillian.junit</groupId>
				<artifactId>arquillian-junit-container</artifactId>
				<version>1.1.8.Final</version>
				<scope>test</scope>
			</dependency>
			<dependency>
				<groupId>org.jboss.arquillian.container</groupId>
				<artifactId>arquillian-tomcat-remote-7</artifactId>
				<version>1.0.0.CR7</version>
				<scope>test</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
</project>
  • Importer le projet dans le Liferay Developer Studio

arquillian1

4.2. Tests units

4.2.1. Création du projet

  • Créer un projet de Portlet « arquillian-portlet »avec l’interface de l’IDE
  • Ne pas utiliser le repertoire par défaut. Le projet de Portlet doit être dans le projet parent contenant le pom parent.
  • Les informations à insérer :
    • Project name : arquillian-portlet
    • Display name : Arquillian
    • Location : %workspace%/arquillian/arquillian-portlet
    • Build type : Maven (liferay-maven-plugin)
    • Artifact version : 1.0.0-SNAPSHOT
    • Group id : fr.sedona.arquillian
    • Active profiles : liferay-6.2
    • Plugin type : Portlet
    • Include sample code : décocher
    • Launch New Portlet Wizard after project is created : décocher
    • Add project to working set : décocher

arquillian2

  • Cliquer sur « Finish » pour obtenir l’arborescence suivante :

arquillian3

  • Ouvrir le fichier « pom.xml » de ce projet Portlet afin d’ajouter les dépendances
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
</dependency>
<dependency>
	<groupId>com.liferay.arquillian</groupId>
	<artifactId>arquillian-liferay-maven-extension</artifactId>
</dependency>
<dependency>
	<groupId>org.jboss.arquillian.junit</groupId>
	<artifactId>arquillian-junit-container</artifactId>
</dependency>
<dependency>
	<groupId>org.jboss.arquillian.container</groupId>
	<artifactId>arquillian-tomcat-remote-7</artifactId>
</dependency>
  • Créer un dossier « test » dans le dossier « src »
  • Créer un dossier « java » et un dossier « resources » dans le dossier « test »
  • Faites un clic droit sur le projet Portlet -> Maven -> Update Project afin que l’IDE prenne en compte ces nouveaux dossiers dans le classpath
  • Créer un fichier nommé « arquillian.xml » dans « src/test/resources » avec
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://jboss.org/schema/arquillian" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
	<container qualifier="tomcat" default="true">
		<configuration>
			<property name="jmxPort">8099</property>
			<property name="host">localhost</property>
			<property name="httpPort">8080</property>
			<property name="user">tomcat</property>
			<property name="pass">tomcat</property>
		</configuration>
	</container>
</arquillian>
  • Voilà, à partir de ce projet, il est possible de faire les tests unitaires/d’intégration avec Arquillian

4.2.2. Test des services Liferay

Testons des services de Liferay avec le framework Arquillian

  • Créer un package « fr.sedona.arquillian.liferay.service » dans « src/test/java »
  • Créer une classe Java « UserLocalServiceTest » dans ce package
package fr.sedona.arquillian.liferay.service;

import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.LocaleUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.PropsUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.model.Company;
import com.liferay.portal.model.User;
import com.liferay.portal.model.VirtualHost;
import com.liferay.portal.service.CompanyLocalServiceUtil;
import com.liferay.portal.service.ServiceContext;
import com.liferay.portal.service.UserLocalServiceUtil;
import com.liferay.portal.service.VirtualHostLocalServiceUtil;

import java.util.Calendar;
import java.util.Locale;

import org.jboss.arquillian.junit.Arquillian;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class UserLocalServiceTest {

	private Company _company;
	private User _user;

	@Before
	public void setUp() throws PortalException, SystemException {
		String companyName = "sedona";
		String virtualHostname = companyName + ".fr";
		String shardDefaultName = GetterUtil.getString(PropsUtil.get(PropsKeys.SHARD_DEFAULT_NAME));
		_company = CompanyLocalServiceUtil.addCompany(companyName, virtualHostname, virtualHostname, shardDefaultName, false, 0, true);
	}

	@After
	public void shutdown() throws SystemException, PortalException {
		UserLocalServiceUtil.deleteUser(_user);
		CompanyLocalServiceUtil.deleteCompany(_company);
		VirtualHost virtualHost = VirtualHostLocalServiceUtil.getVirtualHost(_company.getVirtualHostname());
		VirtualHostLocalServiceUtil.deleteVirtualHost(virtualHost);
	}

	@Test
	public void testAddUser() throws PortalException, SystemException {
		User user = addUser();
		Assert.assertEquals("John", user.getFirstName());
		Assert.assertEquals("Doe", user.getLastName());
	}

	private User addUser() throws SystemException, PortalException {
		boolean autoPassword = true;
		String password1 = StringPool.BLANK;
		String password2 = StringPool.BLANK;
		boolean autoScreenName = true;
		String screeName = StringPool.BLANK;
		long facebookId = 0;
		String openId = StringPool.BLANK;
		Locale locale = LocaleUtil.getDefault();
		String firstName = "John";
		String middleName = StringPool.BLANK;
		String lastName = "Doe";
		int prefixId = 0;
		int suffixId = 0;
		boolean male = true;
		int birthdayMonth = Calendar.JANUARY;
		int birthdayDay = 1;
		int birthdayYear = 1970;
		String jobTitle = StringPool.BLANK;
		long[] groupIds = null;
		long[] orginazationIds = null;
		long[] roleIds = null;
		long[] userGroupIds = null;
		boolean sendMail = false;

		ServiceContext serviceContext = new ServiceContext();
		String emailAddress = "jdoe@sedona.fr";
		User defaulUser = UserLocalServiceUtil.getDefaultUser(_company.getCompanyId());

		_user = UserLocalServiceUtil.addUser(defaulUser.getUserId(), _company.getCompanyId(), autoPassword, password1, password2, autoScreenName, screeName, emailAddress, facebookId, openId, locale, firstName, middleName, lastName, prefixId, suffixId, male, birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds, orginazationIds, roleIds, userGroupIds, sendMail, serviceContext);

		return _user;
	}
}
  • Clic droit sur la classe Run As -> JUnit Test

Dans la console du test unitaire, on peut voir l’appel JMX :

INFOS: Connecting to JMX at service:jmx:rmi:///jndi/rmi://localhost:8099/jmxrmi

De plus, on peut également voir dans la console du serveur Liferay, le déploiement et la suppression de la portlet :

INFO [http-bio-8080-exec-16][HotDeployEvent:145] Plugin arquillian-portlet requires marketplace-portlet
INFO [http-bio-8080-exec-16][HotDeployImpl:217] Deploying arquillian-portlet from queue
INFO [http-bio-8080-exec-16][PluginPackageUtil:1016] Reading plugin package for arquillian-portlet
org.apache.catalina.core.ApplicationContext log
INFOS: Initializing Spring root WebApplicationContext
INFO [http-bio-8080-exec-16][PortletHotDeployListener:344] Registering portlets for arquillian-portlet
INFO [http-bio-8080-exec-16][PortletHotDeployListener:497] 0 portlets for arquillian-portlet are available for use
INFO [http-bio-8080-exec-16][HotDeployEvent:145] Plugin arquillian-portlet requires marketplace-portlet
INFO [http-bio-8080-exec-16][PortletHotDeployListener:525] Unregistering portlets for arquillian-portlet
INFO [http-bio-8080-exec-16][PortletHotDeployListener:565] 0 portlets for arquillian-portlet were unregistered
INFO [http-bio-8080-exec-16][PluginPackageUtil:1016] Reading plugin package for arquillian-portlet

Et le test a été un succès. En changeant le « Assert » par exemple, le test échoue.

5. Conclusion

On a vu ici le test d’une création d’une nouvelle instance avec la création d’un nouvel utilisateur dans cette nouvelle instance.

On pourrait alors imaginer faire les tests sur les services custom développés pour le besoin.

De plus des tests peuvent être effectués sur le hook (Custom Services implementations, Indexer post precessing, Servlet filters et Struts actions).

Cependant les tests puissent fonctionner dans le hook, il faut ajouter la dépendance

<dependency>
    <groupId>biz.aQute</groupId>
    <artifactId>bndlib</artifactId>
    <version>1.50.0</version>
</dependency>

Comme on peut le voir, les tests demandent un serveur Liferay démarré afin qu’ils puissent fonctionner, car le framework Arquillian va déployer l’application puis faire le test et enfin la supprimer du serveur.

Ceci pose soucis dans le fait que les tests effectués par un serveur d’intégration continu auront besoin d’un serveur Liferay toujours démarré pour un projet. S’il y a plusieurs projets, il faudra plusieurs serveurs Liferay démarré.

Compte tenu de cette problèmatique, il est peut être interressant d’explorer une solution permettant de démarrer un Liferay seulement lorsque les tests débutent.

2 solutions peuvent être exploiter :

  • Utilisation du plugin Tomcat dans maven afin de démarrer un serveur Tomcat lorsque les tests démarrent
  • Implémenter dans Arquillian une image Docker contenant Liferay

6. Liens

http://www.liferay.com/fr/web/jardineworks/blog/-/blogs/arquillian-maven

http://arquillian.org/blog/2015/02/03/arquillian-extension-liferay-1-0-0-Alpha1/

You may also like...