Play Framework Custom Integration Test Configuration

I’ve been using the Play Framework 2.3 a lot recently for Java development, and have been finding it really easy to use. However, sometimes documentation is scarce, or doesn’t have an example of what it is I am looking for. In this blog post, I discuss how to create a custom testing configuration, and specify a custom path for your test source files.

The Problem: Integration Tests Too Slow

I was working on a web framework and got to the point where integration tests were taking more than a few seconds to run. This was expected, since I was performing end-to-end testing of several complex components. This isn’t a problem by itself, however, when performing test driven development I like to have the test runner executing in the background, picking up new tests and changes to existing tests as I write. For those of you who aren’t familiar with it, you can have the Typesafe Activator do this with the following command:

./activator ~test

The problem was that the default test runner was executing all of the tests, which was a pain since I would have to wait for all of the slow tests to run to see if there was a problem with any changes I made. What I needed was a way to separate out the unit tests from the more complicated integration tests.

The Start of a Solution

As it turns out, since the Activator uses sbt under the covers, it has access to a default integration testing configuration called IntegrationTest. All you need to do is change your root definition to include a configuration for IntegrationTest in build.sbt, and invoke the default integration test settings:

lazy val root = (project in file(".")).enablePlugins(PlayJava) configs(IntegrationTest)
 
Defaults.itSettings

This defines a new testing configuration called it. To run the integration tests all you would need to do is invoke:

it:test

While this put me on the right track, the problem is that the default IntegrationTest configuration defines the location of integration tests to be /src/it (you can see more about where these directories are by running show it:sourceDirectory and inspect it:sourceDirectory from an Activator prompt). Unfortunately, this layout doesn’t fit well with the rest of the Play Framework source tree. On top of that, when I moved my integration tests into the new directory layout, actually running the integration tests resulted in many hundreds of symbol errors such as:

[error]                 assertThat(contentAsString(result)).isEqualTo("mystring");
[error]                            ^
[error]   symbol: method contentAsString(Result)
[error] /projects/myproject/it/controllers/ControllerTest.java:120: error: cannot find symbol
[error]         running(fakeApplication(inMemoryDatabase()), new Runnable() {
[error]                                 ^
[error]   symbol:   method inMemoryDatabase()
[error] /projects/myproject/it/controllers/ControllerTest.java:98: error: cannot find symbol
[error]                         new FakeRequest(Helpers.POST, "/controller/create")
[error]                             ^
[error]   symbol: class FakeRequest
[error] /projects/myproject/it/controllers/ControllerTest.java:202: error: cannot find symbol

The problem is that the IntegrationTest configuration wasn’t pulling in the dependencies that were defined from the default Test configuration. That was a problem.

The Full Solution

Rather than play around with the IntegrationTest configuration, I figured it would be better to define my own custom configuration. The first step was to define my own configuration called ITest in the build.sbt file. I wanted this to be defined based off of the Test configuration, so I made my configuration extend it:

lazy val root = (project in file(".")).enablePlugins(PlayJava) configs(ITest) settings( inConfig(ITest)(Defaults.testSettings) : _*)
 
lazy val ITest = config("it") extend(Test)

So far so good. This created my new integration testing configuration called ITest. The config("it") portion of the second line defines how you can access that configuration from the Activator. I wanted to keep it simple, and just run it:test for my integration tests (like in the section above), so I kept it as "it".

The next hurdle was to define the proper directory layout. With a Play Framework 2.3.x application, the (partial) directory layout is as follows:

  • app is where all of your application source code (controllers, views, models, etc) goes.
  • test is where all of your testing source code goes.

What I wanted to do was create a new directory called it where all of the source code for integration tests would live. To do that, I added the following line to the build.sbt file:

sourceDirectory in ITest := baseDirectory.value / "/it"

This sort of worked. The problem was that it created other directories under /it for Java and Scala sources. Not quite what I wanted. So I defined three other source directories as follows:

javaSource in ITest := baseDirectory.value / "/it"
 
resourceDirectory in ITest := baseDirectory.value / "/it/resources"
 
scalaSource in ITest := baseDirectory.value / "/it"

This basically mashes both the Scala and Java sources into the /it directory, and names a specific directory for resources under /it/resources.

After syncing the changes to build.sbt, IntelliJ built the new directory structure for me. I moved my integration tests into the /it directory and ran the integration tests only:

[MyProject] $ it:test
[info] Updating {file:/projects/myproject/}root...
[info] Resolving jline#jline;2.11 ...
[info] Done updating.
[info] Passed: Total 57, Failed 0, Errors 0, Passed 57
[success] Total time: 49 s, completed 8-May-2015 1:40:58 AM

Just to be sure it would run the old unit tests (and only the unit tests – I knew there should only be 229 unit tests):

[MyProject] $ test
[info] Passed: Total 229, Failed 0, Errors 0, Passed 229
[success] Total time: 18 s, completed 8-May-2015 1:42:35 AM

Perfect.

Wrapping Up

To create a custom integration testing configuration, I only had to change a few lines in the build.sbt file. All together:

lazy val root = (project in file(".")).enablePlugins(PlayJava) configs(ITest) settings( inConfig(ITest)(Defaults.testSettings) : _*)
 
lazy val ITest = config("it") extend(Test)
 
sourceDirectory in ITest := baseDirectory.value / "/it"
 
javaSource in ITest := baseDirectory.value / "/it"
 
resourceDirectory in ITest := baseDirectory.value / "/it/resources"
 
scalaSource in ITest := baseDirectory.value / "/it"

Now, running integration tests is as easy as it:test while running unit tests remains as test.

More Than One FieldConstructor with Play Framework

Often times in the Play Framework, I need to have more than one custom field constructor. Figuring out how to do this the first time took a lot of work to get it right. Here I discuss how you can create and use more than one custom field constructor in your next Play Framework 2.3.X application.

Background

The Play Framework has a set of helpers that makes building forms quick and easy. These helpers take care of populating your form elements with the correct values based upon some object that you have kicking around. For example, assuming that you have a simple User class that keeps track of user information:

public class User {
    @Constraints.Required
    public String name;
    @Constraints.Required
    public String twitterId;
}

Here there are two attributes we are keeping track of: a user’s name and twitterId. To build a form in the controller, we convert that class into a Form object
and pass it to our HTML template. So the controller for example, may have a method that looks something like this:

public static Result userDetailsForm() {
    Form userForm = Form.form(User.class);
    userForm = userForm.bindFromRequest(request().body().asFormUrlEncoded());
    return ok(views.html.userdetails.render(userForm));
}

This controller does the work of transforming our simple User class into a form object. In this example, we take any data that was passed to the controller in a POST statement, and bind it to the form. We pass that form object to an HTML template called userdetails.scala.html. In the HTML template is some code that actually lets us construct a form quite easily using the Play Framework helpers:

@(userForm: Form[User])
@import views.html.helper._

Simple User Form

@helper.form(action = routes.Application.saveUserDetails()) {
    @inputText(userForm("name"))
    @inputText(userForm("twitterId"))
}

The Problem

When creating a form with the Play Framework, you want your input elements to match the style of the rest of your page. Usually, it’s just a matter of applying the class that you want directly to your input elements. For example, if you are familiar with Bootstrap, you usually want to apply a class such as form-control to your input element. This is accomplished by inserting the element into the open set of parameters when you use the field that you want. You probably also want to use a better descriptor for the field; the label element will let you do that. You may also want to use some placeholder value to show the user an example of what they can type:

@inputText(userForm("name"),
    '_label->"User Name",
    'placeholder->"e.g. John Smith",
    'class->"form-control")
@inputText(userForm("twitterId"),
    '_label->"Twitter ID",
    'placeholder->"e.g. myTwitterID",
    'class->"form-control")

This will append the class information to the form element that you want to create. This is good, since it allows easy customization without having to write the HTML yourself. It also lets Play Framework handle the nitty gritty details of putting values into those input elements so that you don’t have to. This is a life saver when you have select elements that have lots of potential values. The rendered input elements would look something like this:

simple-user-form

Note that to generate the above input, I actually have a template system already in place that is including the correct Bootstrap CSS and Javascript.

This is good, but what happens if you want to create an input group and put an @ sign in front of the Twitter ID like so:

simple-user-form-with-at

To accomplish this, you need a custom field template to generate a FieldConstructor.

Anatomy of a Custom Field Template

Although they sound complicated, a custom field template is nothing more than an HTML template that accepts a helper.FieldElements parameter as input to the form. That’s really about it. Aside from that, they contain the HTML that you define to draw the form element. In the case of our @ sign appearing in front of the Twitter ID field, we need to create a span element before the input box that will render whatever text we pass in. I created an HTML template as a file called twitteridinput.scala.html with this template in it:

@(elements: helper.FieldElements)
<label for="@elements.id">@elements.label</label>
<div class="input-group @if(elements.hasErrors) {has-error}"><span class="input-group-addon">@elements.args.get('_prepend)</span> @elements.input</div>
<div class="info">@elements.infos</div>
@if(elements.hasErrors){
<div class="input-group has-error"><span class="help-block">@elements.errors</span></div>
}

As you can see in the code, the @elements parameter is what holds all of the field elements that get passed to the constructor. The attributes id, label, and infos all get populated from the form field that is passed it (more on that below). Aside from those, is an attribute called hasErrors that is true if there are errors with the field. The errors attribute has the actual error text. As you can see here, I check to see if there are fields with errors, and if so, I generate some friendly HTML to alert the user to the error. Finally, the input attribute contains the code to generate the actual input element that we want.

The real magic is in the “ class. How does it work? In addition to all the other attributes contained within the elements object is args. This contains a list of key -&gt; value pairs. You can put anything you want into that list. We saw this above with the '_label, 'placeholder and 'class parameters. In the case of this simple template, I expect a key called '_prepend which will have whatever you want to be pre-pended to the field. Whatever is in that named attribute will be displayed within the span class.

Using the Template

To use the new custom field template, we have to generate a FieldConstructor for it. This is a helper class in the Play Framework which will take care of a lot of the busywork for us (binding the correct things from the userForm field to the helper.FieldElements). To generate the FieldConstructor, all you need to do is put the following in the userdetails.scala.html template:

@twitterField = @{ FieldConstructor(twitteridinput.f) }

Then, to apply that template to the element that you want, you just need to specify it as the handler for that field. For example, to apply it to our Twitter ID field:

@inputText(userForm("twitterId"),
    '_label->"Twitter ID",
    '_prepend->"@",
    'placeholder->"e.g. myTwitterID",
    'class->"form-control"
)(handler = twitterField, implicitly[Lang])

That’s it! Note that the handler isn’t the only thing we have to specify – we must implicitly pass the language to the template helper too. All together, this is what is needed to generate the simple form:

@(userForm: Form[User])
@import views.html.helper._
@twitterField = @{ FieldConstructor(twitteridinput.f) }

Simple User Form

@helper.form(action = routes.Application.userDetailsForm()) {
    @inputText(userForm("name"),
        '_label->"User Name",
        'placeholder->"e.g. John Smith",
        'class->"form-control")
    @inputText(userForm("twitterId"),
        '_label->"Twitter ID",
        '_prepend->"@",
        'placeholder->"e.g. myTwitterID",
        'class->"form-control"
    )(handler = twitterField, implicitly[Lang])
}

Using this method, you can have several different custom field formats in your template. Simply define the templates you want to use, and apply them as handlers to whatever fields you need when generating your forms.