Wednesday, February 1, 2012

Testing Scalatra with Immutable Specs2

import org.specs2.Specification
import org.specs2.mock.Mockito
import org.scalatra.test.ScalatraTests
import org.eclipse.jetty.testing.ServletTester
import org.specs2.specification.After

class MySpec extends Specification { def is = 
  "My spec must" ^
  "verify that scalatra can be tested" ! Specs().assert()

   * Create a new jetty context each time
   * This lets us mock expectations *per* specification
  case class Specs() extends After with ScalatraTests with Mockito {
    // The servlet tester gives an http context to your tests
    lazy val tester = new ServletTester

    val myMock = mock[MyObject]

    // Register your servlet with the context and inject the mock
    addServlet(new MyServlet(myMock), "/*")

    // don't leave the jetty context hanging around
    def after { tester.stop() }

    // Pay special attention to the "this" keyword, which will provide the
    // ...method in a scope such that "after" may be called for teardown
    def assert() = this {
      // Just some expectation on the mock object
      myMock.get(0) returns true

      get("/") {
        there was one(myMock).get(0)


  1. Hi,

    I think you're misleading the people with this snippet.
    This will create a new server instance for ever example you execute. The tester should be started before all examples execute and stop afterwards.

    And the way you use this then:

    you can get that by depending on scalatra-specs2

  2. Thanks for the pointer. I had some trouble tracking that down in the documentation from the Scalatra Book. The BaseScalatraSpec will simplify my Before and After dependence in my spec.

    In regard to creating a new server instance, the Spec that you reference does not get the full idea across of the problem above. Specifically, if your servlet depends on a mock object, then you cannot rely on the same servlet in each test as the injected mock object must be distinct per fragment. I did not originally have this in my code example and will update it to reflect this.

    Please correct if I am wrong.