Setting up Selenium with Maven

In the team I’m currently working with, the need for regression testing was becoming obvious. The team is working on a web MVC-type framework, and has been running into the results of limited testing: quality wasn’t just low, it was unknown. The usual excuses were there, pressure to release, and interruptions by unplanned work. Usually, the unplanned work is caused by the same lack of quality that is probably caused by pressure to release.

Since this is a web-intensive project where the front-end is not just important, but actually a core part of the software, we decided the first order of business would be to get Selenium tests running on the test/showcase applications. I’ve worked with Selenium before, but not with the GWT front-end technology, and not with the Selenium’s WebDriver APIs. I thought I’d share the process, and some of the issues encountered. In this post, I’ll describe how to get selenium running from a local maven build while still being able to run the unit/micro-tests separately.

Integration into the build

The project is built using Maven, so the first step is to get both the selenium dependencies in there, and be able to run a simple Hello World test from the maven build. The Hello World test was simply an empty Selenium JUnit test that opened the test-showcase, the hello world applet within that, and checked the window title.

package com.qualogy.selenium;

import java.util.concurrent.TimeUnit;

import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class HelloWorldTest {

	WebDriver driver = new FirefoxDriver();
	// WebDriver driver = new HtmlUnitDriver(true);

	String baseUrl = "http://localhost:7070";

	@Before
	public void setUp() throws Exception {
		driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
	}

	@Test
	@Ignore
	public void testTestQafe() throws Exception {
		driver.get(baseUrl + "/test-showcaseapp/QAFEGWTWeb.jsp");
		
		// Open HelloWorld Applet
		driver.findElement(By.id("qafe_menu_applications|system_app")).click();
		driver.findElement(By.id("HelloWorld")).click();
		driver.findElement(By.id("window1|HelloWorld")).click();

		assertEquals("Hello World!", driver.getTitle());
	}

	@After
	public void tearDown() throws Exception {
		driver.quit();
	}
}

Getting this to compile requires you to add the selenium-java dependency into your maven pom.xml file:

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
		</dependency>
		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-java</artifactId>
			<version>2.4.0</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

Note: I initially used the 2.3.0 version of selenium. That version does not work together with FireFox 6, though, and that broke my tests for a bit when I installed that update.

At this point, as long as the application under test is running outside the build, just doing a mvn clean install will run you selenium test, and you’ll see Firefox starting up, doing whatever you’ve defined in the test, and quitting again. Success!

Next, I would really like to get this running without depending on the external process of starting your application outside of your build. I won’t go into the troubles you can have if you’ve tailored your build process for deployment on some proprietary application server (check out the cargo plugin, as a starting point). But luckily, in this case, the system started without any problems by simply doing:

mvn jetty:run

This starts jetty with the default configuration, which mean port 8080, which I don’t want happening when we’ll be running this later on our build server, since that one uses port 8080 itself. More importantly, I don’t want to run these tests with every build. Integration tests using a deployed application are simply too slow to run with every build. Developers should be able to run them locally, but not when they’ve only added new unit-test (and any production code that came with those).

The way to deal with this is to use a maven profile where you can switch certain phases of the build on and off. Another way would be to create a separate maven (sub) project that is there only to run the integration test build, but that was not the direction I chose. So what does such a profile look like?


	<profiles>
		<!-- Profile to enable integration test execution Should be enabled given 
			a -Dintegration.test option when running the build -->
		<profile>
			<id>integration-test</id>
			<activation>
				<property>
					<name>integration.test</name>
				</property>
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>org.mortbay.jetty</groupId>
						<artifactId>maven-jetty-plugin</artifactId>
						<version>${org.mortbay.jetty}</version>
						<configuration>										
							<webAppSourceDirectory>${project.build.directory}/${artifactId}-${version}</webAppSourceDirectory>									
							<webXml>${project.build.directory}/${artifactId}-${version}/WEB-INF/web.xml</webXml>
							<scanIntervalSeconds>10</scanIntervalSeconds>
							<stopKey>foo</stopKey>
							<stopPort>9999</stopPort>
							<connectors>
								<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
									<port>7070</port>
								</connector>
							</connectors>
						</configuration>
						<executions>
							<execution>
								<id>start-jetty</id>
								<phase>pre-integration-test</phase>
								<goals>
									<goal>run</goal>
								</goals>
								<configuration>
									<scanIntervalSeconds>0</scanIntervalSeconds>
									<daemon>true</daemon>
								</configuration>
							</execution>
							<execution>
								<id>stop-jetty</id>
								<phase>post-integration-test</phase>
								<goals>
									<goal>stop</goal>
								</goals>
							</execution>
						</executions>
					</plugin> 
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-surefire-plugin</artifactId>
						<configuration>
							<!-- Skip the normal tests, we'll run the integration-tests only -->
							<skip>true</skip>
							<excludes>
							</excludes>
							<includes>
								<include>com/example/selenium/**</include>
							</includes>
						</configuration>
						<executions>
							<execution>
								<phase>integration-test</phase>
								<goals>
									<goal>test</goal>
								</goals>
								<configuration>
									<skip>false</skip>
									<includes>
										<include>com/example/selenium/**</include>
									</includes>
									<excludes>
									</excludes>
								</configuration>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>
		<profile>
			<!-- Added a micro/unit test profile that is run by default
				 so that we can override the surefire excludes in an
				 integration test build -->
			<id>micro-test</id>
			<activation>
				<property>
					<name>!integration.test</name>
				</property>				
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-surefire-plugin</artifactId>
						<configuration>
							<excludes>
								<exclude>com/example/selenium/**</exclude>
							</excludes>
						</configuration>
					</plugin>
				</plugins>
			</build>
		</profile>
	</profiles>	

That’s quite a lot of XML, but in short the integrationt-test profile does the following:

  • Activate the integration-test profile when the -Dintegration.test property is set
  • Start jetty (jetty:run) before doing the integration-test phase
  • Stop jetty after the integration-test phase
  • Do NOT run any unit tests if the integration-test profile is enabled
  • DO run all the integration tests in the package com.example.selenium when the integration-test profile is enabled

Which is great, and works. But when we then run the normal build, the selenium tests are still run (and fail). So that’s where the micro-test profile in the snippet above comes in: it excludes the selenium tests from the normal surefire testrun.

So now if we do mvn clean install all our normal micro-tests are run, and if we do mvn -Dintegration.test clean install the micro-tests are skipped and the selenium tests are run.

Now we are ready to write a real test, but I’ll save that for a future post.

2 thoughts on “Setting up Selenium with Maven”

Leave a Reply