14

I have a Play Framework 2.3 project in which I'd like to separate unit tests and functional tests as follows:

  1. running sbt test should run unit tests and exclude integration tests
  2. running sbt it:test should run integration tests only

The Scala documentation suggests using project/Build.scala, but I'd like to use combination of build.sbt and project/Build.scala, so my configuration looks like this (I have also tried putting all of configuration into Build.scala):

build.sbt

....

libraryDependencies ++= Seq(
  "com.typesafe.play" %% "play-json" % "2.2.3",
  "org.scalatest" %% "scalatest" % "2.1.5" % "it, test",
  "org.mockito" % "mockito-all" % "1.9.5" % "it, test"
)

def funTestFilter(name: String): Boolean = ((name endsWith "ItTest") || (name endsWith "IntegrationTest"))
def unitTestFilter(name: String): Boolean = ((name endsWith "Test") && !funTestFilter(name))

testOptions in IntegrationTest := Seq(Tests.Filter(funTestFilter))

testOptions in Test := Seq(Tests.Filter(unitTestFilter))

project/Build.scala

import sbt._

object Build extends Build {

  lazy val root =
    Project("root", file("."))
      .configs( IntegrationTest )
      .settings( Defaults.itSettings : _* )

}

Under this configuration running sbt test does exclude my integration test (which ends with IntegrationTest) but running sbt it:test finds no tests.

The article suggests putting files in a specific path, but I don't know what the equivalent path is for the Play project.

The standard source hierarchy is used:

src/it/scala for Scala sources
src/it/java for Java sources
src/it/resources for resources that should go on the integration test classpath
chooban
  • 9,018
  • 2
  • 20
  • 36
Mike
  • 1,176
  • 3
  • 14
  • 26

2 Answers2

26

The first part - about setting up integration tests in build.sbt - is described in another question What would be the minimal setup of IntegrationTest configuration in sbt 0.13.x? -- it boils down to the following entries in build.sbt:

Defaults.itSettings

lazy val root = project.in(file(".")).configs(IntegrationTest)

Since a Play project is defined as follows:

lazy val root = (project in file(".")).enablePlugins(PlayScala)

you should add configs and you're done.

lazy val root = project in file(".") enablePlugins(PlayScala) configs(IntegrationTest) 

Do reload or restart the sbt/activator session and execute it:test.

[it-play] $ it:test
[info] Updating {file:/Users/jacek/sandbox/it-play/}root...
[info] Resolving jline#jline;2.11 ...
[info] Done updating.
[success] Total time: 1 s, completed Sep 13, 2014 7:12:27 PM

As to the place where the integration tests should go in a Play project, the short answer is do show it:sourceDirectory in the shell, i.e. src/it with scala, java and resources directories beneath to contain respective sources.

Add test framework library in it configuration, i.e. in build.sbt there should be something similar to - note the it configuration:

"org.specs2" %% "specs2" % "2.4.2" % "it"

Do reload the sbt session or restart it, and it:test should work with Specs2 tests for all tests under src/it/scala.

To narrow what tests should be executed, add your filter - make sure it's after the line with Defaults.itSettings as the order does matter and one can override the other:

val funTestFilter: String => Boolean = { name =>
  (name endsWith "ItTest") || (name endsWith "IntegrationTest")
}

testOptions in IntegrationTest += Tests.Filter(funTestFilter)

Do reload or restart sbt and execute it:test. It should work. Do show it:testOptions to ensure your setup's loaded properly:

[it-play] $ show it:testOptions
[info] List(Filter(<function1>))

The entire build.sbt that worked for me is as follows - it's created by activator new it-play play-scala:

name := """it-play"""

version := "1.0-SNAPSHOT"

lazy val root = project in file(".") enablePlugins(PlayScala) configs(IntegrationTest)

scalaVersion := "2.11.1"

libraryDependencies ++= Seq(
  jdbc,
  anorm,
  cache,
  ws,
  "org.specs2" %% "specs2" % "2.4.2" % "it"
)

Defaults.itSettings

val funTestFilter: String => Boolean = { name =>
  (name endsWith "ItTest") || (name endsWith "IntegrationTest")
}

testOptions in IntegrationTest += Tests.Filter(funTestFilter)
Community
  • 1
  • 1
Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
  • Thanks!!!. My project doesn't know what "PlayScala" is, this is the error I get: "/web/api-checkout/build.sbt:28: error: not found: value PlayScala", however, it seems to work without it, I don't know what problems that might cause otherwise, I have not found any issues yet. – Mike Sep 15 '14 at 17:50
  • Using Play 2.3.9, I followed the examples but my IntegrationSpec.scala cannot compile because it cannot find PlaySpecification i.e. class IntegrationSpec extends PlaySpecification. Any idea why SBT didn't include play libs to "it" phase? Also, I have added this to build.sbt, "org.specs2" %% "specs2-core" % "3.5" % "it, test", "org.specs2" %% "specs2-junit" % "3.5" % "it, test", "org.specs2" %% "specs2-mock" % "3.5" % "it, test" Thanks – thlim Nov 24 '15 at 09:32
  • ok I added play-test to "it" i.e. "com.typesafe.play" %% "play-test" % "2.3.9" % "it" – thlim Nov 24 '15 at 10:14
  • 4
    It's confusing that Play's integration test is "src/it" instead of "/it", because it would make more sense if it were alongside "/app" and "/test" in the base directory, as per [the Play Anatomy](https://www.playframework.com/documentation/2.4.x/Anatomy). I moved my integration tests there by changing the setting in `build.sbt` to read `sourceDirectory in IntegrationTest := baseDirectory.value / "it"`. Then it finds the usual `it/java`, `it/scala`, `it/resources` directories. – mikebridge Jan 14 '16 at 15:59
  • That is quite strange naming suggestions - with a case insensitive match, `UnitTest` also ends with `ItTest`, and the fact that `ItTest` resolves to `IntegrationTestTest` ;-) – conny Jan 23 '18 at 07:22
13

This is what I ended up doing, which allows me to:

  1. keep my integration tests in play's /test directory
  2. run sbt test for Unit Tests and sbt fun:test for functional/integration tests

Here is the build.sbt, no need for a project/Build.scala

libraryDependencies ++= Seq(
  ...
  "com.typesafe.play" %% "play-json" % "2.2.3",
  "org.scalatest" %% "scalatest" % "2.1.5" % "test",
  "org.mockito" % "mockito-all" % "1.9.5" % "test"
)

lazy val FunTest = config("fun") extend(Test)

def funTestFilter(name: String): Boolean = ((name endsWith "ItTest") || (name endsWith "IntegrationTest"))
def unitTestFilter(name: String): Boolean = ((name endsWith "Test") && !funTestFilter(name))

lazy val root = project in file(".") configs(FunTest) settings( inConfig(FunTest)(Defaults.testTasks) : _*)

testOptions in FunTest := Seq(Tests.Filter(funTestFilter))

testOptions in Test := Seq(Tests.Filter(unitTestFilter))
k1eran
  • 4,492
  • 8
  • 50
  • 73
Mike
  • 1,176
  • 3
  • 14
  • 26
  • 1
    I really like this solution because I can just clone and go in any IDE of choice. If I create a new testing folder then as of today I have to either put IDE specific stuff in my build (ick) or have an extra IDE step to tell the IDE about my new folder (not great either). – rancidfishbreath Dec 09 '15 at 20:16