Writing web user interface tests

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());
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.75.217