Until now, you have written specifications for the business and HTTP layers. However, these specifications do not cover user experience. For instance, you might want to define the following user story as a testable specification: a user browses the item list, clicks on a button to add a new item, enters the name and price of the item to create and submit the form, and the created item is displayed.
You can define such specifications using Selenium WebDriver (https://code.google.com/p/selenium/); this library allows you to programmatically drive a web browser. Play applications automatically depend on Selenium WebDriver as well as on FluentLenium, a library that provides a fluent interface for the WebDriver. For instance, a specification for the user story described previously can be defined as follows:
import play.api.test.{WithBrowser, PlaySpecification}
class UISpec extends PlaySpecification {
"A user" should {
"add a new item to the item list" in new WithBrowser {
browser.goTo(controllers.routes.Items.list().url)
// No item yet
browser.$("ul").isEmpty must beTrue
// Click on the "Add a new item" button
val formUrl = controllers.routes.Items.createForm().url
browser.$(s"""a[href="$formUrl"]""").click()
browser.submit("form",
"name" -> "Play Framework Essentials",
"price" -> "42")
// The item is displayed
browser.$("body")
.getText must contain ("Play Framework Essentials: 42.00 €")
}
}
}
The Java equivalent code is as follows:
import org.junit.Test;
import play.test.WithBrowser;
import static org.fest.assertions.Assertions.assertThat;
import static play.test.Helpers.*;
public class UITest extends WithBrowser {
@Test
public void addItem() {
browser.goTo(controllers.routes.Items.list().url());
assertThat(browser.$("ul").isEmpty()).isTrue();
String formUrl = controllers.routes.Items.createForm().url();
browser.$("a[href="" + formUrl + ""]").click();
browser.fill("input[name=name]").with("Play Framework Essentials");
browser.fill("input[name=price]").with("42");
browser.submit("form");
assertThat(browser.$("body").getText())
.contains("Play Framework Essentials: 42.00 €");
}
}
The test definition is essentially the same in Scala and Java. It uses the FluentLenium API (with some additional convenient methods in Scala). It starts by browsing the URL that shows the list of items. It checks whether there are any items. It clicks on a link to create a new item. It fills and submits the form. It checks whether the content of the created item is displayed.
The Scala test uses the WithBrowser
scope that starts the application and an HTTP server, creates a Selenium WebDriver (available within the scope through the browser
member), runs the test, and stops the application. As with WithApplication
, the WithBrowser
scope can take the application to start as a parameter, so you can provide a custom application whose configuration is tweaked for your testing environment:
"a test specification" in new WebBrowser(app = yourFakeApplication) { // your test code goes here }
The Java version uses the WithBrowser
class that starts the application and an HTTP server and creates a Selenium WebDriver (available through the browser
field) before running the test and stops the server after the test has been executed. You can use a custom WebDriver by overriding the provideBrowser
method (just like the provideFakeApplication
method).
Actually, the previously mentioned addItem
test specification does not pass if your global object inserts bootstrap data into the database when the application starts, as explained in Chapter 2, Persisting Data and Testing, (the test expects that the item list is empty at the beginning). Indeed the default fake application uses the default global object, namely the same as your real application. Another problem is that the test specification inserts data into the database, but you might want to use a test database instead of the real database.
You can solve both problems by using a custom fake application, which itself uses custom global settings:
"add a new item to the item list" in new WithBrowser( app = fakeApplication( withGlobal = Some(Helpers.defaultGlobal), additionalConfiguration = Helpers.inMemoryDatabase ) ) { … }
The Java equivalent code is as follows:
@Override
protected FakeApplication provideFakeApplication() {
return fakeApplication(inMemoryDatabase(), fakeGlobal());
}
18.216.96.94