For integration testing on the siark.com webapp I’m using JBehave 3 and Selenium. As an initial test all I’m going to do is test that the home page displays the message “Welcome to the siark.com website.” when a user navigates to /igallery, the root of the siark.com website.I’m using the Page Object pattern to construct the tests.
1 Write a scenario and save it in a file called home.story in src/test/resources/com/siark/igallery/stories
Scenario: User opens home page
Given the user opens the home page
Then the home page should be displayed
2 Create a generic page class that I call IGalleryPage. The java file is created in src/test/java/com/siark/igallery.
package com.siark.igallery.pages;
import org.jbehave.web.selenium.SeleniumPage;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.condition.ConditionRunner;
public class IGalleryPage extends SeleniumPage {
public IGalleryPage(Selenium selenium, ConditionRunner conditionRunner) {
super(selenium, conditionRunner);
}
}
3 Create a HomePage class that deals with the home page specifically. The java file is created in src/test/java/com/siark/igallery/pages.
package com.siark.igallery.pages;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.condition.ConditionRunner;
public class HomePage extends IGalleryPage {
public HomePage(Selenium selenium, ConditionRunner conditionRunner) {
super(selenium, conditionRunner);
}
public void open() {
open("/igallery/");
}
public void verifyPage() {
textIsVisible("Welcome to the siark.com website.");
}
}
4 Now create a class called IGallerySteps that defines the annotations used in the story. The java file is created in src/test/java/com/siark/igallery.
package com.siark.igallery;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import com.siark.igallery.pages.FindSteps;
import com.siark.igallery.pages.HomePage;
import com.siark.igallery.pages.PageFactory;
public class IGallerySteps {
private final PageFactory pageFactory;
private HomePage home;
public IGallerySteps(PageFactory pageFactory) {
this.pageFactory = pageFactory;
}
@Given("the user opens the home page")
public void theUserOpensTheHomePage() {
home = pageFactory.home();
home.open();
}
@Then("the home page should be displayed")
public void theHomePageShouldBeDisplayed(){
home.verifyPage();
}
}
5 A factory class called PageFactory is used to get page classes. The java class is created in src/test/java/com/siark/igallery/pages.
package com.siark.igallery.pages;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.condition.ConditionRunner;
public class PageFactory {
private final Selenium selenium;
private final ConditionRunner conditionRunner;
public PageFactory(Selenium selenium, ConditionRunner conditionRunner) {
this.selenium = selenium;
this.conditionRunner = conditionRunner;
}
public HomePage home() {
return new HomePage(selenium, conditionRunner);
}
}
6 To configure and run the tests, an embedable runnable class is created, which I have called IGalleryStories. The java file is created in src/test/java/com/siark/igallery.
package com.siark.igallery;
import static java.util.Arrays.asList;
import static org.jbehave.core.io.CodeLocations.codeLocationFromClass;
import static org.jbehave.core.reporters.StoryReporterBuilder.Format.CONSOLE;
import static org.jbehave.core.reporters.StoryReporterBuilder.Format.HTML;
import static org.jbehave.core.reporters.StoryReporterBuilder.Format.TXT;
import static org.jbehave.core.reporters.StoryReporterBuilder.Format.XML;
import java.util.List;
import org.jbehave.core.Embeddable;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.LoadFromClasspath;
import org.jbehave.core.io.StoryFinder;
import org.jbehave.core.junit.JUnitStories;
import org.jbehave.core.reporters.ConsoleOutput;
import org.jbehave.core.reporters.StoryReporter;
import org.jbehave.core.reporters.StoryReporterBuilder;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InstanceStepsFactory;
import org.jbehave.core.steps.SilentStepMonitor;
import org.jbehave.web.selenium.SeleniumConfiguration;
import org.jbehave.web.selenium.SeleniumContext;
import org.jbehave.web.selenium.SeleniumStepMonitor;
import com.siark.igallery.pages.PageFactory;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.condition.ConditionRunner;
public class IGalleryStories extends JUnitStories {
private Selenium selenium = SeleniumConfiguration.defaultSelenium();
private ConditionRunner conditionRunner = SeleniumConfiguration.defaultConditionRunner(selenium);
private PageFactory pageFactory = new PageFactory(selenium, conditionRunner);
private SeleniumContext seleniumContext = new SeleniumContext();
@Override
public Configuration configuration() {
Class<? extends Embeddable> embeddableClass = this.getClass();
return new SeleniumConfiguration()
.useSelenium(selenium)
.useSeleniumContext(seleniumContext)
.useStepMonitor(new SeleniumStepMonitor(selenium, seleniumContext, new SilentStepMonitor()))
.useStoryLoader(new LoadFromClasspath(embeddableClass))
.useStoryReporterBuilder(new StoryReporterBuilder() {
@Override
public StoryReporter reporterFor(String storyPath, Format format) {
if (format == CONSOLE) {
return new ConsoleOutput() {
@Override
public void beforeScenario(String title) {
seleniumContext.setCurrentScenario(title);
super.beforeScenario(title);
}
};
} else {
return super.reporterFor(storyPath, format);
}
}
}
.withCodeLocation(CodeLocations.codeLocationFromClass(embeddableClass))
.withDefaultFormats()
.withFormats(CONSOLE, TXT, HTML, XML));
}
@Override
public List<CandidateSteps> candidateSteps() {
return new InstanceStepsFactory(configuration(), new IGallerySteps(pageFactory), new FailingScenarioScreenshotCapture(selenium))
.createCandidateSteps();
}
@Override
protected List<String> storyPaths() {
return new StoryFinder().findPaths(codeLocationFromClass(this.getClass()).getFile(), asList("**/*.story"), null);
}
}
This class is actually the same class provided on the JBehave website 🙂
7 The last class is also from the JBehave website and is the FailingScenarioScreenshotCapture used in IGalleryStories. It is also created in src/test/java/com/siark/igallery.
package com.siark.igallery;
import org.jbehave.core.annotations.AfterScenario;
import org.jbehave.core.annotations.AfterScenario.Outcome;
import org.jbehave.web.selenium.PerScenarioSeleniumSteps;
import com.thoughtworks.selenium.Selenium;
public class FailingScenarioScreenshotCapture extends PerScenarioSeleniumSteps {
public FailingScenarioScreenshotCapture(Selenium selenium) {
super(selenium);
}
@AfterScenario(uponOutcome = Outcome.FAILURE)
public void afterFailingScenario() throws Exception {
String home = System.getenv("HOME");
selenium.captureScreenshot(home+"/failedScenario.png");
}
}
8 Finally I need to add a dependency for jbehave-web-selenium, the maven-jetty-plugin plugin so that the Jetty application server can be started and stopped and the igallery.war file can be deployed to it, the selenium-maven-plugin plugin to start and stop the Selenium server and lastly the jbehave-maven-plugin plugin to run the integration tests. The Jetty server will listen on port 8080 for HTTP requests, and the Selenium server will listen on port 4444.
<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.siark.igallery</groupId>
<artifactId>igallery</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>Siark iGallery Webapp</name>
<url>http://www.siark.com</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.jbehave.web</groupId>
<artifactId>jbehave-web-selenium</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>igallery</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<webApp>${project.build.directory}/igallery.war</webApp>
<webAppConfig>
<contextPath>/igallery</contextPath>
</webAppConfig>
</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>
<configuration>
<stopKey>stopJetty</stopKey>
<stopPort>9966</stopPort>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>selenium-maven-plugin</artifactId>
<version>1.0.1</version>
<executions>
<execution>
<id>start-selenium</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start-server</goal>
</goals>
<configuration>
<background>true</background>
<debug>false</debug>
<logOutput>true</logOutput>
</configuration>
</execution>
<execution>
<id>stop-selenium</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop-server</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jbehave</groupId>
<artifactId>jbehave-maven-plugin</artifactId>
<version>3.0</version>
<executions>
<execution>
<id>run-stories</id>
<phase>integration-test</phase>
<configuration>
<includes>
<include>**/*Stories.java</include>
</includes>
<scope>test</scope>
</configuration>
<goals>
<goal>run-stories-as-embeddables</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jbehave.web</groupId>
<artifactId>jbehave-web-selenium</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<properties>
<org.springframework.version>3.0.4.RELEASE</org.springframework.version>
</properties>
</project>