my thoughts on programming and other nerdy stuff

How to integrate Concordion in Play Framework and write acceptance tests in natural language


Screen Shot 2013-07-05 at 10.24.25After start­ing to read “Spec­i­fi­ca­tion by Exam­ple”, I wanted to have a go a writ­ing accep­tance tests for my Play Frame­work projects.  I stum­bled onto Con­cor­dion (http://​www​.con​cor​dion​.org/), because it is sim­i­lar to Cucum­ber but writ­ten in and for Java.

Con­cor­dion is sim­i­lar to Cucum­ber but focuses on readability.

Con­cor­dion inte­grates as Unit tests (which is the pri­mary focus of this post), and lets you write the Tests in Nat­ural Lan­guage in HTML Files so that it also becomes self documenting.

I got most of how to inte­grate Con­cor­dion from Craig Aspinall in Aus­tralia (@aspinall) who had writ­ten a Blog post about it, but which was no long “online”.   (he sent me a link to the mark­down so I could try it out — thank Craig!)

Inte­grat­ing Concordion

You first need to add the depen­dency to your Build.scala, and then add a line so that the SBT copies the Con­cor­dion HTML files:

import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

    val appName         = "computer-database"
    val appVersion      = "1.0"

    val appDependencies = Seq(
       "org.concordion" % "concordion" % "1.4.2" % "test"  ,

    val main = play.Project(appName, appVersion, appDependencies).settings(
      // Next, you need to instruct SBT to copy the Concordion HTML files to the target folder
        unmanagedClasspath in Test <+= (baseDirectory) map { bd => Attributed.blank(bd / "test") }

Telling Play where to out­put the Test results to

This turned out to be a chal­lenge. If you do not set the concordion.output.dir sys­tem prop­erty, then they are out­puted to your Java temp direc­tory. Craig sug­gested set­ting it in the Build.scala, but as it turns out the most recent Ver­sion of Play forks the Tests into a sep­a­rate process and loses the set­ting. So I ended up adding this to my Global Set­tings class:

import play.Application;
import play.GlobalSettings;
public class Global extends GlobalSettings {

    public void onStart(play.Application arg0) {
        if (arg0.isTest())
            System.setProperty("concordion.output.dir", "target/test-reports/concordion");


Note: I think I dis­cov­ered another Bug in Play, because although this works, if I run “test-only” it doesn’t!!!

The Test

The key to get­ting Play to rec­og­nize and run Unit Tests is the @Test anno­ta­tion, so it seems that the eas­i­est way to get Play to run the Con­cor­dion tests is to add a method like this to your Con­cor­dion fix­ture class:

    public void runThisTest() {

I ended up imple­ment­ing one of the tuto­r­ial exam­ples from here: http://​www​.con​cor​dion​.org/​T​u​t​o​r​i​a​l​.​h​tml

You write the test case in HTML, and use <span> tags with Con­cor­dion ele­ments to spec­ify the paramters and assertions:

<html xmlns:concordion="http://www.concordion.org/2007/concordion">

        <link href="../concordion.css" rel="stylesheet" type="text/css" />


        <h1>Splitting Names</h1>

            To help personalise our mailshots we want to have the first name
            and last name of the customer. Unfortunately the customer data
            that we are supplied only contains full names.

            The system therefore attempts to break a supplied full name into
            its constituents by splitting around whitespace.

        <div class="example">


                The full name
                <span concordion:execute="#result = split(#TEXT)">John Smith</span>
                will be broken into first name
                <span concordion:assertEquals="#result.firstName">John</span>
                and last name
                <span concordion:assertEquals="#result.lastName">Smith</span>.


Then you write a fix­ture class with the Tests (and the @Test “hack” I mentioned).

import org.junit.Test;
import play.mvc.Result;

import java.util.HashMap;
import java.util.Map;

import static org.fest.assertions.Assertions.assertThat;
import static play.test.Helpers.*;

import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;
import org.junit.Test;

public class ConcordianTestFixture {

    public Result split(String fullName) {
        Result result = new Result();
        String[] words = fullName.split(" ");
        result.firstName = words[0];
        result.lastName = words[1];
        return result;

    class Result {
        public String firstName;
        public String lastName;

    public void runThisTest() {

When you run the tests, then the result­ing HTML page it out­puted (as pre­vi­ously defined) to your target/test-reports directory. Here is an exam­ple of a failed test (for a suc­cess­ful one, see the image at the start of the post):

Screen Shot 2013-07-05 at 10.30.39

I hope you try this out!  Let me know if you have more ideas or tips…

If you have read this far, you may as well fol­low me on Twit­ter:

Author: poornerd

Tech­nol­o­gist, Entre­pre­neur, Vision­ary, Pro­gram­mer :: Grad­u­ated from USC (Uni­ver­sity of South­ern Cal­i­for­nia) with a degree in Com­puter Sci­ence. After 10+ years of free­lance con­sult­ing and pro­gram­ming, he co-founded Site­Force AG eBusi­ness Solu­tions in 1999 in Munich (München), Ger­many.


  1. Note: I think I dis­cov­ered another Bug in Play, because although this works, if I run “test-only” it doesn’t!!!”

    If I’m not mis­taken, your Global code would only be run if you started up a fake appli­ca­tion in your test using the Helpers class.

    This is all very inter­est­ing, though. I’ve been play­ing around with Cucum­ber, but this cer­tainly war­rants a look. Have you seen a change in how much work it takes to pro­duce a test in Cucum­ber and in Concordion?

    • Ok, but if I run “play test” it works, and if I “play ‘test-only mytest’” it doesn’t get called. Maybe there is a bet­ter way to set the prop­erty? It also worked when I set it in the run­This­Test() method.

      Regard­ing Cucum­ber — I can’t really say, because this is my first go at the whole BDD/TDD thing.

  2. nice post thank you some­times TDD seems hard to imple­ment in play…, btw, which chrome theme are you using?

Leave a Reply

Required fields are marked *.