Sunday 27 July 2014

Configuring integration tests in Scala Play project

I struggled, for several days I have to admit, to configure integration test to a Scala Play project.
With Maven it is usually a relatively trivial, well documented step. SBT also does have excellent article, but it does not work for Play project without tweaking. All I found on Web so far were not working, partial, usually poorly commented posts, often for outdated versions of SBT or Play.

I succeed so I am sharing. I share a full complete solution, with plentiful comments, tested and known to work with recent, summer 2014, versions of Scala, Play and SBT; that is 2.10.x for Scala, 2.2.x for Play Framework, 13.x for SBT.

Goal

Configure special group of tests, let’s call them integration tests, so they are run separately, on a special command only, not as a part of a normal build.

  • Integration tests to be in additional, separate source directory.
    For Play projects it is suggested “it” placed directly under project root, so let’s follow it.
  • Integration tests are not run in normal build, with ordinary tests.
    They will not run as a part on Jenkins (CI platform) project build.
  • Integration tests are run only on a special Play command (equivalent to Maven Profiles facility)
    Useful to have them as a separate Jenkins task.
  • No special test tagging, or file naming pattern is required
  • Special complication - integration tests are dependent on unit test, some helper classes are be shared, so integration test settings has to be aware of ordinary test sources.
  • Try to reuse library dependencies already defined for ordinary tests, so no extra dependencies configuration is required.

Commands

Play console:

it:compile

..to compile just integration tests

it:test

..run just integration tests

test

..run unit tests as usual

Useful: Re-run idea so IntelliJ recognises “it” as an additional test source directory so it can
syntax coloring etc is available and integration tests can be run from IDE.

Build.scala

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

object ApplicationBuild extends Build {

  val appName         = "fooapp"
  val appVersion      = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    "joda-time" % "joda-time" % "2.1",
    "org.scalatest" % "scalatest_2.10" % "2.0" % "test, it",
    "org.mockito" % "mockito-core" % "1.9.5" % "test, it"
  )

Dependencies were shortened.

Note the the test, it qualifier! It is essential, just test is not enough. And no qualifier would indeed cause mixing test with production dependencies.

Define additional source directory for integration tests. This tests are not run in normal build, with unit tests.

  /** integration test settings */
  def itSettings = {
    sourceDirectories in IntegrationTest <+= baseDirectory( _ / "it")
    sourceDirectory in IntegrationTest <<= baseDirectory( _ / "it")
    scalaSource in IntegrationTest <<= baseDirectory( _ / "it")
  }

Perhaps setting both sourceDirectories and sourceDirectory is not essential and only one would do. Explicitly setting scalaSource however is essential.

To define paths now using expression baseDirectory / "it" - is deprecated in SBT 13.x, the baseDirectory( _ / "it") or baseDirectory { _ / "it" } is recommended.

  lazy val IntegrationTestAltConf = config("it") extend(Test)

Based on original Play IntegrationTest. The difference is it is dow derived from Test (unit test) configuration and not Runtime configuration. The original definition is lazy val IntegrationTest = config("it") extend(Runtime) in sbt.Configurations but this way it was not it:compile does not recognize test classes, like TestHelpers used in my test. It is essential for projects where integration tests are not independent but depends on some shared test classes with other tests.

  lazy val main = play.Project(appName, appVersion, appDependencies)
    .settings(javaOptions in Test ++= Seq(
      "-XX:MaxPermSize=512M",
      "-Xms256M",
      "-Xmx512M",
      "-Xss1M"
    ))
    .settings(testOptions in Test += Tests.Argument("-oF"))

..and many other usual Scala project setting. Shortened. Here goes integration test setup:

    .configs(IntegrationTestAltConf)

It is a must, otherwise it will not recognize main sources and main libs. If the integration test were fully independent, shared nothing with unit tests, then standard IntegrationTest could be used.

    .settings(Defaults.itSettings : _*)

This makes command like it:compile and it:run possible from Play console

    .settings(itSettings)

Set additional separate directory for integration tests, see itSettings() method above.

}

That should be it. Don’t forget to run

reload

in Play console to check the new Build.scala validity

Useful: Re-run idea so IntelliJ recognises “it” as an additional test source directory so it can
syntax coloring etc is available and integration tests can be run from IDE.

I found it very useful to have SBT sources attached to my Play project. Sources are attached to binary library dependencies by Play command:

idea with-sources=yes

But this does not include SBT libraries. In my struggle with integration test configuration I had to check sources very often as documentation and examples are still rare or outdated. I’m not sure with Play but underlaying SBT can be persuaded to download its sources and link them to your Build.scala. Type in Play console:

update-sbt-classifiers
gen-idea sbt-classifiers

Source: http://stackoverflow.com/questions/17127367/sbt-sources-in-idea
The article is written for Sbt, but works for Play project without tweaks

Downside is, any subsequent re-run of idea or idea with-sources=yes will break the link to sources.

Written with StackEdit.