<-- home

Integration Testing with Maven, Jetty and Selenium 2

For the last few days, I was busy with preparing a integration test environment for our PrimeFaces showcase. Showcase project was already a Maven project, so I made a little research about maven phases, maven plugins, Selenium and Jetty and figured out how can I make things work. You can see similar blog posts on Google about the topic. I looked at most of them and they helped me on the way.

First of all, maven already has 3 phases for integration testing: pre-integration-test, integration-test, post-integration-test. So I thought that on the pre-integration-test I can start Jetty and deploy the showcase, on the integration-test phase I can run the Selenium tests and on the post-integration-test I can stop Jetty. These steps can be done with Maven Jetty Plugin quite easily.

Another issue I had to deal with was separating integration tests from unit tests and running them only once at their own phases. I used Failsafe Maven Plugin to run the integration tests. Failsafe plugin is just like the Surefire plugin but it’s designed for integration testing. They explain the Failsafe quite well:

If you use the Surefire Plugin for running tests, then when you have a test failure, the build will stop at the integration-test phase and your integration test environment will not have been torn down correctly. The Failsafe Plugin is used during the integration-test and verify phases of the build lifecycle to execute the integration tests of an application. The Failsafe Plugin will not fail the build during the integration-test phase thus enabling the post-integration-test phase to execute.

I also separated unit tests and integration tests by placing integration tests into the package “integration”, excluding test in the “integration” package for Surefire, and including them for Failsafe. So Surefire will run only the unit tests, which are not in the “integration” package, and Failsafe will run the tests in the “integration” package only.

Here is part of the pom for the plugins:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-surefire-plugin</artifactId>
	<version>2.11</version>
	<configuration>
		<excludes>
		<!-- Exclude integration tests within (unit) test phase. -->
		<exclude>**/integration/**/*.java</exclude>
		</excludes>
		<encoding>UTF-8</encoding>
	</configuration>
	</plugin>

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-failsafe-plugin</artifactId>
	<version>2.11</version>
	<configuration>
		<includes>
        		<include>**/integration/**/IntegrationTest*.java</include
				<include>**/integration/**/*IntegrationTest.java</include>
		</includes>
		<encoding>UTF-8</encoding>
	</configuration>
	<executions>
		<execution>
			<goals>
				<goal>integration-test</goal>
				<goal>verify</goal>
			</goals>
		</execution>
	</executions>
</plugin>

<plugin>
	<groupId>org.mortbay.jetty</groupId>
	<artifactId>maven-jetty-plugin</artifactId>
	<version>6.1.16</version>
	<configuration>
		<scanIntervalSeconds>10</scanIntervalSeconds>
		<stopPort>9966</stopPort>
		<stopKey>stop</stopKey>
		<webAppConfig>
			<contextPath>/prime-showcase</contextPath>
		</webAppConfig>
	</configuration>
	<executions>
		<execution>
			<id>start-jetty</id>
			<phase>pre-integration-test</phase>
			<goals>
				<goal>run</goal>
			</goals>
			<configuration>
				<daemon>true</daemon>
			</configuration>
		</execution>
		<execution>
			<id>stop-jetty</id>
			<phase>post-integration-test</phase>
			<goals>
				<goal>stop</goal>
			</goals>
		</execution>
	</executions>
</plugin>
  • Within Surefire’s configuration, I excluded the test classes which are in “integration” package.
  • Within Failsafe’s configuration, I included the test classes which are in “integration” package and starts / ends with IntegrationTest. integration-test and verify goals are defined for integration-test phase.
  • Within Jetty Plugin’s configuration, run and stop goals of the plugin are defined for pre-integration and post-integration phases respectively. I do not explain the other parts, you can find information about them in plugins’ web sites.

Here is a sample integration test class resides under src/test.

package com.prime.showcase.integration.ajaxengine;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import java.util.concurrent.TimeUnit;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.FluentWait;

public class AjaxCounterIntegrationTest {

	protected WebDriver driver;

	private String testUrl = "http://localhost:8080/prime-showcase/ui/pprCounter.jsf";

	@Before
	public void before() {
		driver = new FirefoxDriver();
	}

	@After
	public void after() {
		driver.quit();
	}

	@Test
	public void shouldIncreaseCounter() {
		driver.get(testUrl);

		WebElement button = driver.findElement(By.id(("j_idt14:j_idt17")));
		button.click();

		new FluentWait<WebDriver>(driver).withTimeout(10, TimeUnit.SECONDS).pollingEvery(2, TimeUnit.SECONDS).until(new ExpectedCondition<Boolean>() {
			public Boolean apply(WebDriver wd) {
				WebElement element = wd.findElement(By.id("j_idt14:txt_count"));
				return element.getText().equals("1");
			}
		});

		final WebElement numberText = driver.findElement(By.id(("j_idt14:txt_count")));
		assertThat(numberText.getText(), equalTo("1"));
		button.click();

	}
}

With this configuration, when I type the “mvn integration-test” command from the terminal, Maven builds the project, runs the unit tests, starts Jetty and deploys the application, runs integration tests and stops Jetty.

You can also look the the pom.xml of the showcase project when we update it on the SVN repository.

ps: Here are the dependencies for the example test class:

<dependency>
	<groupId>org.seleniumhq.selenium</groupId>
	<artifactId>selenium-java</artifactId>
	<version>2.15.0</version>
</dependency>

<dependency>
	<groupId>org.hamcrest</groupId>
	<artifactId>hamcrest-all</artifactId>
	<version>1.1</version>
	<scope>test</scope>
</dependency>

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.8.2</version>
	<scope>test</scope>
</dependency>