Saturday, June 13, 2009

Grails, Selenium, Maven and integration testing

Today I wanna share with you my experience of configuring Selenium RC (with Java client drivers), Maven, Surefire to develop and run integration tests.

Creating project structure

  1. Create "demo" directory in your projects dir an step inside it.
  2. Create basic Grails application with Maven by running:
mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-4:generate -DarchetypeGroupId=org.grails -DarchetypeArtifactId=grails-maven-archetype -DarchetypeVersion=1.0 -DgroupId=example -DartifactId=maven-grails-demo
Fix Grails application pom.xml with tips I described in my previous post.
  1. Create integration-test module by running:
mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.meongrails -DartifactId=integration-test
And don't forget to remove App and AppTest from sources that were generated by Maven.
  1. If everything was Ok, you should still be in your main project dir "demo". Let's create multi-module pom.xml in project root dir with following content:
    <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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.meongrails</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>demo</artifactId>
<packaging>pom</packaging>
<modules>
<module>demo-app</module>
<module>integration-test</module>
</modules>
</project>
  1. Now run "mvn install" from project root directory, and see
    "BUILD SUCCESSFUL" :)

Adding necessary dependencies to maven poms

We are going to modify only <build> sections of each pom. So If you extending your existing application with integration tests you'll need to make just few adjustments.
  1. Grails application pom (our demo-app). Go to <build> -> <plugins> section and insert jetty plugin:
    <plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.10</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<stopKey>foo</stopKey>
<stopPort>9999</stopPort>
</configuration>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>run-war</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>

We'll use it to launch our Grails application in the "integration-test" phase. And will click with Selenium on it, but a bit later about all that.
  1. Integration tests module. Since all it's supposed to be custom, I'll post here entire pom:
    <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>com.meongrails</groupId>
<artifactId>integration-test</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>integration-test</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium.client-drivers</groupId>
<artifactId>selenium-java-client-driver</artifactId>
<version>1.0-beta-2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<executions>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<sources>
<fileset>
<directory>${pom.basedir}/src/test</directory>
<includes>
<include>**/*.groovy</include>
</includes>
</fileset>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>selenium-maven-plugin</artifactId>
<executions>
<execution>
<phase>pre-integration-test</phase>
<goals>
<goal>start-server</goal>
</goals>
<configuration>
<background>true</background>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>surefire-it</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>seleniumhq</id>
<name>seleniumhq</name>
<url>http://nexus.openqa.org/content/repositories/releases/</url>
</repository>
</repositories>
</project>
I propose to run "mvn integration-test" from project root, and see what will happen now. There are no tests yet, but Maven should resolve all dependencies and pass all execution phases. "BUILD SUCCESSFUL" means that we could go on :)

Preparing simple functionality for our tests

Import our Maven project into Intellij IDEA as I described in my previous post.
Now I'm creating domain class Message:
public class Message {
String text
}
And controller for it:
class MessageController {
def scaffold = true
}
That's it, our application is ready to be tested.

Writing first Selenium test

Switch to integration-test module, and create "integration-test/test/java/com/meongrails/MessageTest.groovy":
package com.meongrails

import com.thoughtworks.selenium.SeleneseTestCase

public class MessageTest extends SeleneseTestCase {
public void setUp() {
setUp "http://localhost:8080/demo-app/", "*firefox"
}
void testCreateMessage() {
selenium.with {
speed = "1000"
open "message"
waitPage()
assertEquals "Message List", title
click "//a[text()=\"New Message\"]"
waitPage()
assertEquals "Create Message", title
type "text","Message Text ;)"
click "//input[@value=\"Create\"]"
waitPage()
assertEquals "Show Message", title
assertTrue isTextPresent ("Message Text ;)")
}
}
private def waitPage() {
selenium.waitForPageToLoad "30000"
}
}

Running tests

Maven

Run "mvn integration-test" from project root directory and enjoy :). But since your project is going to grow fast (I hope so) or you already big enough, you could experience problems with out of memory error (PermGen space) due to Groovy dynamism. You could easy solve this by adding "MAVEN_OPTS=-Xmx512m -XX:MaxPermSize=256m" to your environment variables.

Intellij IDEA

  1. Go to "Maven" context (at the right edge).
  2. Run your application "demo -> Modules -> demo-app -> Plugins -> grails -> grails:run-app" or just run IDEA grails configuration which should be already available.
  3. Run Selenium with " demo -> Modules -> integration-test -> Plugins -> selenium -> selenium:start-server"
  4. Execute your MessageTest.
Intellij IDEA allows you to save run configuration for ease of further use (selenium:start-server and grails:run-app could be saved as preconfigured). Also you there is no need to restart your application each time you made some changes. Grails will take care of it as usual.

0 comments:

Post a Comment