I'm a coder from way back starting mostly with Java and moving on to cooler things like Rails. I like to dabble with newer technologies and languages. I do also run a fair bit to balance out the coding but am not very good at it as evidenced by my finishing times. Ricky is a DZone MVB and is not an employee of DZone and has posted 15 posts at DZone. You can read more from them at their website. View Full User Profile

PhantomJS with Play

09.20.2013
| 5875 views |
  • submit to reddit
You have two options for browser-based testing, with the Play framework out-of-box: Firefox or HtmlUnit.

When you generate a new Play application, you get the following integration test. Switch the HTMLUNIT constant to FIREFOX to begin using Firefox through webdriver.

 
    @Test
    public void test() {
        running(testServer(3333, fakeApplication(inMemoryDatabase())), HTMLUNIT, new Callback() {
            public void invoke(TestBrowser browser) {
                browser.goTo("http://localhost:3333");
                assertThat(browser.pageSource()).contains("Your new application is ready.");
            }
        });
    }
HtmlUnit does have a couple advantages over a real browser like Firefox.
  1. It's faster.
  2. It's headless, therefore it doesn't need a display. It's useful in CI environments like Travis. Yes you can use Xvfb but that's just another dependency.  HtmlUnit is also handy when used locally so you don't have numerous browser windows popping up when running tests.
The biggest drawback is that the JavaScript support within HtmlUnit is pretty average. I've always run into issues using it when I've done anything remotely complicated in JavaScript. It uses Mozilla's Rhino under the covers. 
Enter PhantomJS. It's a headless WebKit browser with a JavaScript API. Being WebKit based, I've found that it supports JavaScript much better than HtmlUnit.  
So how do you integrate it into your Play application? In a few steps:
 
  1. First, you need to install PhantomJS. There are a number of ways to achieve this, but I just use brew (http://brew.sh/) on OSX. So I run the following command.  
    brew install phantomjs
    On the PhantomJS website http://phantomjs.org/, there are instructions for other Operating Systems.

  2. Add GhostDriver, which is an implementation of WebDriver for PhantomJS, to your Play application.  If you have a Build.scala file (Play 2.1.X and optionally Play 2.2.X), add Ghostdriver by editing the Build.scala file and adding the dependency like this. 
      val appDependencies = Seq(
        // Add your project dependencies here,
        javaCore,
        javaJdbc,
        javaEbean,
        "com.github.detro.ghostdriver" % "phantomjsdriver" % "1.0.4" % "test"
      )
    
    Or if you're on Play 2.2.X and above, and only have only a build.sbt file, add the GhostDriver by editing the build.sbt file and adding the dependency like this:
    libraryDependencies ++= Seq(
      javaJdbc,
      javaEbean,
      cache,
      "com.github.detro.ghostdriver" % "phantomjsdriver" % "1.0.4" % "test"
    )     
    
  3. Change the test to use GhostDriver by passing the PhantomJSDriver class instead of the HTMLUNIT constant:    
        @Test
        public void test() {
            running(testServer(3333, fakeApplication(inMemoryDatabase())), org.openqa.selenium.phantomjs.PhantomJSDriver.class, new Callback() {
                public void invoke(TestBrowser browser) {
                    browser.goTo("http://localhost:3333");
                    assertThat(browser.pageSource()).contains("Your new application is ready.");
                }
            });
        }
    
  4. Re-run the test: 
    $ play test
    If everything has worked correctly, you should have output similar to this:
    2013-09-19 11:29:06.727 phantomjs[4326:707] *** WARNING: Method userSpaceScaleFactor in class NSView is deprecated on 10.7 and later. It should not be used in new applications. Use convertRectToBacking: instead.
    PhantomJS is launching GhostDriver...
    [INFO  - 2013-09-19T01:29:06.832Z] GhostDriver - Main - running on port 15803
    [INFO  - 2013-09-19T01:29:06.993Z] Session [e04d1ec0-20ca-11e3-89ad-45bbd56cac5d] - _decorateNewWindow - page.settings: {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Macintosh; PPC Mac OS X) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.2 Safari/534.34","webSecurityEnabled":true}
    [INFO  - 2013-09-19T01:29:06.993Z] Session [e04d1ec0-20ca-11e3-89ad-45bbd56cac5d] - page.customHeaders:  - {}
    [INFO  - 2013-09-19T01:29:06.993Z] Session [e04d1ec0-20ca-11e3-89ad-45bbd56cac5d] - CONSTRUCTOR - Desired Capabilities: {"platform":"ANY","browserName":"phantomjs","version":""}
    [INFO  - 2013-09-19T01:29:06.993Z] Session [e04d1ec0-20ca-11e3-89ad-45bbd56cac5d] - CONSTRUCTOR - Negotiated Capabilities: {"browserName":"phantomjs","version":"1.9.2","driverName":"ghostdriver","driverVersion":"1.0.4","platform":"mac-10.8 (Mountain Lion)-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}}
    [INFO  - 2013-09-19T01:29:06.993Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: e04d1ec0-20ca-11e3-89ad-45bbd56cac5d
    [INFO  - 2013-09-19T01:29:07.949Z] ShutdownReqHand - _handle - About to shutdown
    [info] IntegrationTest
    [info] + IntegrationTest.test
    [info]
    [info]
    [info] Total for test IntegrationTest
    [info] Finished in 0.0 seconds
    
  5. You can also take screenshots with PhantomJS, which is very useful for diagnosing failures and debugging. 
        @Test
        public void test() {
            running(testServer(3333, fakeApplication(inMemoryDatabase())), org.openqa.selenium.phantomjs.PhantomJSDriver.class, new Callback() {
                public void invoke(TestBrowser browser) {
                    browser.goTo("http://localhost:3333");
                    assertThat(browser.pageSource()).contains("Your new application is ready.");
                    browser.takeScreenShot("/tmp/screenshot.jpg");
                }
            });
        }
 I've created a sample app here with the dependencies set: https://github.com/codingricky/sample-play. Happy testing!
Published at DZone with permission of Ricky Yim, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)