Ensar Basri Kahveci

overly distributed

Integration Testing with Maven, Jetty and Selenium 2

Posted at — Dec 25, 2011

integration-testing - jetty - maven - maven surefire - maven failsafe - selenium 

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 integration package, excluding test in theintegrationpackage for Surefire, and including them for Failsafe. So Surefire will run only the unit tests, which are not in theintegrationpackage, and Failsafe will run the tests in theintegration` 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>

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>
comments powered by Disqus