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>