{{ !articles[0].partner.isSponsoringArticle ? "Platinum" : "Portal" }} Partner

How the play framework test runner works

After working for a while with the play framework we had the need to write better integration tests.
By better I mean easier to maintain. I like Selenium but there are re-usability and maintenance issues. Anyway without going any further into details I had to figure out how the @tests page works.

The @tests page for one of the sample applications

First thing let me assure you: there is no magic. Actually it is quite easy to follow once you understand how the play works. The only complicated part is the selenium bit.

TestRunner is one of the default modules in the play framework together with secure, crud and some others. You can find it under PLAY_HOME/modules. You can also browse the code on github.

Adding the routes - plugin code

As far as I understand the module only contains a plugin

Under testRunner there are four folders
  • src - the plugin source code
  • public - contains all the static html as in every play project
  • app - controllers and views
  • firephoque - just a library folder

Let's go into the details.

Under src there is a file called play.plugins that tells the framework the plugin implementation is play.modules.testrunner.TestRunnerPlugin.

This class extends PlayPlugin and has 3 methods onLoad(), onRoutesLoaded(), onApplicationReady().

This methods are invoked by the framework itself...guess when? :)

onLoad() simply adds the test folder to the classpath for the application and for all the modules.

onRoutesLoaded() adds the test routes to the application

 public void onRoutesLoaded() {
    Router.addRoute("GET", "/@tests", "TestRunner.index");
    Router.addRoute("GET", "/@tests.list", "TestRunner.list");
    Router.addRoute("GET", "/@tests/{<.*>test}", "TestRunner.run");
    Router.addRoute("POST", "/@tests/{<.*>test}", "TestRunner.saveResult");
    Router.addRoute("GET", "/@tests/emails", "TestRunner.mockEmail");

and the last one, onApplicationReady() simply prints the test url to the console.

Note: This line is then used in auto-test mode to detect the application is ready. Look at base.py lines 204-215

The other class in the same folder,called FirePhoque.java is used in auto-test mode to simulate the browser.

To summarise, so far we have the application started with some additional routes pointing at TestRunner methods.

Where it's all happening - controllers and views

Let's have a look at the TestRunner controller. You can see there all the methods added as routes in TestRunnerPlugin. Let's look at what some of methods do.

is using TestEngine to figure out which ones are unit test, functional tests and selenium tests. Then it just puts the three lists in the render scope and the view page index.html renders them nicely in what you see when you go to /@tests

This is a list of all the tests rendered as text. As far as i know is only used by auto-test

This is the method that actually runs the java tests and sends back the results.

This method saves a file in the test-results folder for every test that has been executed

The Java Tests Execution


Unit and functional test are easy enough to understand. If you want to have more details, look at the run method in TestEngine

The Selenium Tests Execution

Now here comes the tricky part

Files ending in .test.html are selenium tests and they are passed to the TestRunner controller as .test.html.suite.

The controller simply execute

test = test.substring(0, test.length() - 6);
render("TestRunner/selenium-suite.html", test);

Now if you look at selenium-suite.html you'll be scratching your head because there is nothing else that a table with a row and a link to the test.

To understand you need to look at index.html lines 362-380.
This code is simply loading the selenium-core runner (pure DHTML) in an iframe, is making it visible and is calling the Testrunner controller to get the result every 2 seconds until it either returns an HTTP 500 (for a test failure) or an HTTP 200 (you guessed, test passed)

a picture is worth a thousand words

The frame in the mean time calls the controller (lines 82-100) loads the html test as any other application page, using the TemplateLoader.

That is why you can use tags in your selenium tests! Isn't that great?

Now the selenium-core runs the html test and calls the controller saveResult method (lines 120-131) that saves a file either .passed or .failed depending on the test outcome.
That will allow the flow in index.html to move on to the next test.

Quite good isn't it?

Thanks for reading, please leave any feedback / corrections you feel.


From http://www.devinprogress.info/2011/04/how-play-framework-test-runner-works.html

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks