Question

I am using Specs2 with play 2.2.1 built with Scala 2.10.2 (running Java 1.7.0_51). I have been reading about how to do setup/teardown with Specs2. I have seen examples using the "After" trait as follows:

class Specs2Play extends org.specs2.mutable.Specification {
  "this is the first example" in new SetupAndTeardownPasswordAccount {
    println("testing")
  }
}

trait SetupAndTeardownPasswordAccount extends org.specs2.mutable.After {
  println("setup")

  def after  = println("teardown ")
}

This works fine, except that all of my tests are using "in new WithApplication". It seems what I need is to have an object which is both a "WithApplication" and an "After". Below does not compile, but is essentially what I want:

trait SetupAndTeardownPasswordAccount extends org.specs2.mutable.After with WithApplication

So, my question is, how do I add setup/teardown to my tests which are already using "in WithApplication"? My primary concern is that all of our tests make use of fake routing like this (so they need the With Application).

val aFakeRequest = FakeRequest(method, url).withHeaders(headers).withBody(jsonBody)
val Some(result) = play.api.test.Helpers.route(aFakeRequest)
result
Was it helpful?

Solution

This is the code for WithApplication:

abstract class WithApplication(val app: FakeApplication = FakeApplication()) extends Around with Scope {
  implicit def implicitApp = app
  override def around[T: AsResult](t: => T): Result = {
    Helpers.running(app)(AsResult.effectively(t))
  }
}

It's actually quite easy to modify this to suit your needs without creating a bunch of other traits. The missing piece here is the anonymous function t, which you provide the implementation for in your tests (using WithApplication). It would be nice to make WithApplication a little more robust to be able to execute arbitrary blocks of code before and after the tests, if necessary.

One approach could be to create a similar class to WithApplication that accepts two anonymous functions setup and teardown that both return Unit. All I really need to do is modify what's happening inside AsResult.effectively(t). To keep this simple, I'm going to remove the app parameter from the parameter list, and use FakeApplication always. You don't seem to be providing a different configuration, and it can always be added back.

abstract class WithEnv(setup: => Unit, teardown: => Unit) extends Around with Scope {
  implicit def implicitApp = app
  override def around[T: AsResult](t: => T): Result = {
    Helpers.running(app)(AsResult.effectively{
         setup
         try {
             t
         } finally {
             teardown
         }
    })
  }
}

Instead of simply calling the anonymous function t, I first call setup, then t, then teardown. The try/finally block is important because failed tests in specs2 throw exceptions, and we want to be sure that teardown will be executed no matter what the outcome.

Now you can easily setup test environments using functions.

import java.nio.files.{Files, Paths}

def createFolder: Unit = Files.createDirectories(Paths.get("temp/test"))

def deleteFolder: Unit = Files.delete("temp/test")

"check if a file exists" in new WithEnv(createFolder, deleteFolder) {
    Files.exists(Paths.get("temp/test")) must beTrue
}

(This might not compile, but you get the idea.)

OTHER TIPS

If your after method doesn't need anything from the WithApplication trait you can mix in your specification the AfterExample trait and define the after behaviour for the whole spec:

import org.specs2.specification._

class Specs2Play extends org.specs2.mutable.Specification with AfterExample {
  "this is the first example" in new SetupAndTeardownPasswordAccount {
    pending("testing")
  }

  trait SetupAndTeardownPasswordAccount extends WithApplication

  def after = println("cleanup")
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top