##What is this?
This is an example of how to use the Arquillian Liferay Extension.
This example will be executed in the following environment:
- Tomcat Server 9.0.6
- JMX enabled and configured. This means that if you do not use the automatically downloaded and configured
Liferay + Tomcat bundle, you should deploy and activate the following modules which are not active by default
in the plain vanilla Liferay + Tomcat bundle:
org.apache.aries.jmx:org.apache.aries.jmx.api:1.1.5
org.apache.aries:org.apache.aries.util:1.1.1
org.apache.aries.jmx:org.apache.aries.jmx.core:1.1.8
com.liferay:com.liferay.hot.deploy.jmx.listener:2.0.0-SNAPSHOT
- Tomcat Manager installed and configured.
- JMX enabled and configured. This means that if you do not use the automatically downloaded and configured
Liferay + Tomcat bundle, you should deploy and activate the following modules which are not active by default
in the plain vanilla Liferay + Tomcat bundle:
- Liferay 7.1.0
- JUnit 4.12
##Creating a Liferay Portlet for testing
###Add Liferay, Portlet and OSGi dependencies to pom.xml
...
<dependencies>
....
<dependency>
<groupId>com.liferay.portal</groupId>
<artifactId>portal-service</artifactId>
<version>${liferay.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.liferay.portal</groupId>
<artifactId>util-bridges</artifactId>
<version>${liferay.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.liferay.portal</groupId>
<artifactId>util-taglib</artifactId>
<version>${liferay.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.liferay.portal</groupId>
<artifactId>util-java</artifactId>
<version>${liferay.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>${osgi.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.portlet</groupId>
<artifactId>portlet-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
....
</dependencies>
...
###Create a OSGi Service
For testing purpouse we are going to create a new OSGI Service that add two numbers.
First of all, we need to create a new Interface
package com.liferay.arquillian.sample.service;
public interface SampleService {
public int add(int a, int b);
}
And a new implementation for the interface
package com.liferay.arquillian.sample.service;
import org.osgi.service.component.annotations.Component;
@Component(immediate = true, service = SampleService.class)
public class SampleServiceImpl implements SampleService {
@Override
public int add(int a, int b) {
return a + b;
}
}
###Create a Liferay MVC Portlet
Create a MVC Portlet that call the previous service
package com.liferay.arquillian.sample.portlet;
import com.liferay.portal.kernel.portlet.PortletURLFactoryUtil;
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.kernel.theme.ThemeDisplay;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.Portlet;
import javax.portlet.PortletRequest;
import javax.portlet.PortletURL;
import com.liferay.arquillian.sample.service.SampleService;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
@Component(
property = {
"com.liferay.portlet.display-category=category.sample",
"com.liferay.portlet.instanceable=false",
"javax.portlet.display-name=Arquillian Sample Portlet",
"javax.portlet.init-param.template-path=/",
"javax.portlet.init-param.view-template=/view.jsp",
"javax.portlet.name=arquillian_sample_portlet",
"javax.portlet.resource-bundle=content.Language",
"javax.portlet.security-role-ref=power-user,user"
},
service = Portlet.class
)
public class SamplePortlet extends MVCPortlet {
public void add(ActionRequest actionRequest, ActionResponse actionResponse)
throws Exception {
ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
WebKeys.THEME_DISPLAY);
int firstParameter = ParamUtil.getInteger(
actionRequest, "firstParameter");
int secondParameter = ParamUtil.getInteger(
actionRequest, "secondParameter");
int result = _sampleService.add(firstParameter, secondParameter);
PortletURL portletURL = PortletURLFactoryUtil.create(
actionRequest, "arquillian_sample_portlet", themeDisplay.getPlid(),
PortletRequest.RENDER_PHASE);
portletURL.setParameter(
"firstParameter", String.valueOf(firstParameter));
portletURL.setParameter(
"secondParameter", String.valueOf(secondParameter));
portletURL.setParameter("result", String.valueOf(result));
actionRequest.setAttribute(WebKeys.REDIRECT, portletURL.toString());
}
@Reference(unbind = "-")
public void setSampleService(SampleService sampleService) {
_sampleService = sampleService;
}
private SampleService _sampleService;
}
Create a view.jsp file:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %><%@
taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %><%@
taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %><%@
taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>
<portlet:defineObjects />
<liferay-theme:defineObjects />
<%
int firstParameter = ParamUtil.getInteger(request, "firstParameter", 1);
int secondParameter = ParamUtil.getInteger(request, "secondParameter", 1);
int result = ParamUtil.getInteger(request, "result");
%>
<portlet:actionURL name="add" var="portletURL" />
<p>
<b>Sample Portlet is working!</b>
</p>
<aui:form action="<%= portletURL %>" method="post" name="fm">
<aui:input inlineField="<%= true %>" label="" name="firstParameter" size="4" type="int" value="<%= firstParameter %>" />
<span> + </span>
<aui:input inlineField="<%= true %>" label="" name="secondParameter" size="4" type="int" value="<%= secondParameter %>" />
<span> = </span>
<span class="result"><%= result %></span>
<aui:button type="submit" value="add" />
</aui:form>
###Create a BND file for deployment
For testing we will use BND to create the package to be deployed, so we need to add a maven dependency to the pom.xml
...
<dependencies>
...
<dependency>
<groupId>org.jboss.shrinkwrap.osgi</groupId>
<artifactId>shrinkwrap-osgi</artifactId>
<version>1.0.0-alpha-1</version>
<scope>test</scope>
</dependency>
...
</dependencies>
...
And create a bnd-basic-portlet-test.bnd file:
Bundle-Name: Basic Portlet Test
Bundle-SymbolicName: com.liferay.arquillian.sample
Bundle-Version: 1.0.0
Export-Package: com.liferay.arquillian.sample
Include-Resource:\
target/classes,\
META-INF/resources=src/main/resources/META-INF/resources
Require-Capability:\
osgi.extender;filter:="(&(osgi.extender=jsp.taglib)(uri=http://java.sun.com/portlet_2_0))",\
osgi.extender;filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/aui))",\
osgi.extender;filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/portlet))",\
osgi.extender;filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/theme))",\
osgi.extender;filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/ui))",\
osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))"
-dsannotations: *
##Configuration Steps
You can follow this guide to enable your JMX configuration in tomcat
In the next example you can see a example of a setenv file that enable JMX in a Tomcat in the port 8099 without authentication:
CATALINA_OPTS="$CATALINA_OPTS -Dfile.encoding=UTF8 -Djava.net.preferIPv4Stack=true -Dorg.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false -Duser.timezone=GMT -Xmx1024m -XX:MaxPermSize=256m"
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}"
...
<dependencies>
....
<dependency>
<groupId>com.liferay.arquillian</groupId>
<artifactId>arquillian-container-liferay</artifactId>
<version>1.0.0.Final-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
....
</dependencies>
...
Create a simple test that:
- Inject SampleService
- Test SampleService
package com.liferay.arquillian.test;
import com.liferay.portal.kernel.exception.PortalException;
import java.io.File;
import java.io.IOException;
import com.liferay.arquillian.containter.liferay.remote.enricher.Inject;
import com.liferay.arquillian.sample.service.SampleService;
import com.liferay.shrinkwrap.osgi.api.BndProjectBuilder;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(Arquillian.class)
public class BasicPortletIntegrationTest {
@Deployment
public static JavaArchive create() {
BndProjectBuilder bndProjectBuilder = ShrinkWrap.create(
BndProjectBuilder.class);
bndProjectBuilder.setBndFile(new File("bnd-basic-portlet-test.bnd"));
bndProjectBuilder.generateManifest(true);
return bndProjectBuilder.as(JavaArchive.class);
}
@Test
public void testAdd() throws IOException, PortalException {
int result = _sampleService.add(1, 3);
Assert.assertEquals(4, result);
}
@Inject
private SampleService _sampleService;
}
To create a functional test in Liferay with the Arquillian Liferay Extension we are going to follow this guide
First of all, we need to configure the pom.xml file to add the graphene-webdriver dependencies
...
<dependencies>
....
<dependency>
<groupId>org.jboss.arquillian.graphene</groupId>
<artifactId>graphene-webdriver</artifactId>
<version>2.1.0.Alpha2</version>
<type>pom</type>
<scope>test</scope>
</dependency>
....
</dependencies>
...
First, create a profile for each desired browser
...
properties>
<browser>phantomjs</browser>
</properties>
...
<profiles>
...
<profile>
<id>firefox</id>
<properties>
<browser>firefox</browser>
</properties>
</profile>
<profile>
<id>chrome</id>
<properties>
<browser>chrome</browser>
</properties>
</profile>
...
</profiles>
Next you need to setup arquillian.xml in order to change the Arquillian settings for browser selection. Add the following to the arquillian.xml:
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<extension qualifier="webdriver">
<property name="browser">${browser}</property>
</extension>
</arquillian>
package com.liferay.arquillian.test;
import com.liferay.portal.kernel.exception.PortalException;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import com.liferay.arquillian.containter.liferay.remote.enricher.Inject;
import com.liferay.arquillian.installportlet.annotation.InstallPortlet;
import com.liferay.arquillian.sample.service.SampleService;
import com.liferay.shrinkwrap.osgi.api.BndProjectBuilder;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* @author Cristina González
*/
@RunAsClient
@RunWith(Arquillian.class)
public class BasicPortletFunctionalTest {
@Deployment
public static JavaArchive create() {
BndProjectBuilder bndProjectBuilder = ShrinkWrap.create(
BndProjectBuilder.class);
bndProjectBuilder.setBndFile(new File("bnd-basic-portlet-test.bnd"));
bndProjectBuilder.generateManifest(true);
return bndProjectBuilder.as(JavaArchive.class);
}
@Test
public void testAdd() throws IOException, PortalException {
browser.get(_portlerURL.toExternalForm());
firstParamter.clear();
firstParamter.sendKeys("2");
secondParameter.clear();
secondParameter.sendKeys("3");
add.click();
Assert.assertEquals("5", result.getText());
}
@Test
public void testInstallPortlet() throws IOException, PortalException {
browser.get(_portlerURL.toExternalForm());
String bodyText = browser.findElement(By.tagName("body")).getText();
Assert.assertTrue(
"The portlet is not well deployed",
bodyText.contains("Sample Portlet is working!"));
}
@InstallPortlet(name ="arquillian_sample_portlet")
private URL _portlerURL;
@Inject
private SampleService _sampleService;
@FindBy(css = "button[type=submit]")
private WebElement add;
@Drone
private WebDriver browser;
@FindBy(css = "input[id$='firstParameter']")
private WebElement firstParamter;
@FindBy(css = "span[class='result']")
private WebElement result;
@FindBy(css = "input[id$='secondParameter']")
private WebElement secondParameter;
If we want to Inject the URL of the container using the annotation @ArquillianResource (and we don't want to define a deployment), we can use one of these solutions (if we are using the Arquillian Liferay Extension)
- Create a deployment method in our test class.
- Configure Arquillian using the graphene url property (via arquillian.xml, arquillian.properties or System Properties)
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
...
<extension qualifier="graphene">
<property name="url">http://localhost:8080</property>
</extension>
...
</arquillian>
##Create Code Coverage Reports with Jacoco
To create a code coverage report with Jacoco for the Liferay integration tests, we are going to follow this guide
###Create a Jacoco profile in your Maven pom.xml
Create a new profile jacoco that configure he plugin ''jacoco-maven-plugin'' with the dependencies ''org.jacoco.core'' and ''arquillian-jacoco''.
...
<profile>
<id>jacoco</id>
<dependencies>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.core</artifactId>
<version>0.7.4.201502262128</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-jacoco</artifactId>
<version>1.0.0.Alpha8</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.4.201502262128</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
###Generate the HTML Jacoco report
mvn test -Pjacoco jacoco:report
The simplest reason for this is that Liferay just has not fully started up and activated all necessary modules yet. You want to check this possibility first.
Otherwise, maybe the JMX-related OSGi modules (Apache Aries JMX, Liferay JMX hot-deploy) listed at the beginning of this document have been installed in the OSGi runtime (status "Installed" or "Resolved" in GoGo shell), but for some reason not started, which sometimes can happen for unknown reasons. I this case
- connect to the GoGo shell locally via
telnet 11311
(alternatively, use GoGo shell from the Liferay admin GUI), - list the corresponding modules via
lb | grep 'JMX|Aries'
and - if there are any modules with a status other than "Active", start each service via its numerical ID using
the command
start <ID>
(e.g.start 950
).
Then check again with lb | grep 'JMX|Aries
. Everything should be listed as "Active" now and the Arquillian
tests should be able to use JMX hot-deploy and run as expected.