Specs2/Guice issue in Play 2.4.0 functional tests

487 views Asked by At

I'm having an issue with dependencies apparently bleeding between tests, which is causing most of the tests to fail. In each case, debugging shows the first app created in a test class is used for all tests, and this is resulting in the failures.

I've tried adding isolated and sequential and this has had no effect.

Am I doing something remarkably stupid or subtly stupid?

For example, here's SubjectNotPresentTest.scala

class SubjectNotPresentTest extends AbstractViewTest {

  "show constrained content when subject is not present" in new WithApplication(testApp(handler())) {
    val html = subjectNotPresentContent(FakeRequest())

    private val content: String = Helpers.contentAsString(html)
    content must contain("This is before the constraint.")
    content must contain("This is protected by the constraint.")
    content must contain("This is after the constraint.")
  }

  "hide constrained content when subject is present" in new WithApplication(testApp(handler(subject = Some(user())))) {
    val user = new User("foo", Scala.asJava(List.empty), Scala.asJava(List.empty))
    val html = subjectNotPresentContent(FakeRequest())

    private val content: String = Helpers.contentAsString(html)
    content must contain("This is before the constraint.")
    content must not contain("This is protected by the constraint.")
    content must contain("This is after the constraint.")
  }
}

GuiceApplicationBuilder is used in a parent class is used to create the app for testing.

val app = new GuiceApplicationBuilder()
          .bindings(new DeadboltModule())
          .bindings(bind[HandlerCache].toInstance(LightweightHandlerCache(handler)))
          .overrides(bind[CacheApi].to[FakeCache])
          .in(Mode.Test)
          .build()

You can see an example of the failures at https://travis-ci.org/schaloner/deadbolt-2-scala/builds/66369307#L805

All tests can be found at https://github.com/schaloner/deadbolt-2-scala/tree/master/code/test/be/objectify/deadbolt/scala/views

Thanks, Steve

1

There are 1 answers

0
Steve Chaloner On BEST ANSWER

It looks like the problem is caused when the current Play application is statically referenced in a test environment in which there are multiple applications - even if they are logically separate.

Because components can't be injected (to the best of my knowledge) into templates, I created a helper object which uses Play.current.injector to define a couple of vals.

  val viewSupport: ViewSupport = Play.current.injector.instanceOf[ViewSupport]
  val handlers: HandlerCache = Play.current.injector.instanceOf[HandlerCache]

(It's also not possible, TTBOMK, to inject into objects, otherwise I could just inject the components into the object and everyone could go home).

A better approach is to expose what is required as an implicit.

object ViewAccessPoint {

    private[deadbolt] val viewStuff = Application.instanceCache[ViewSupport]
    private[deadbolt] val handlerStuff = Application.instanceCache[HandlerCache]

    object Implicits {
        implicit def viewSupport(implicit application: Application): ViewSupport = viewStuff(application)
        implicit def handlerCache(implicit application: Application): HandlerCache = handlerStuff(application)
    }
}

In the view, import the implicits and you're good to go.

@import be.objectify.deadbolt.scala.DeadboltHandler
@import be.objectify.deadbolt.scala.ViewAccessPoint.Implicits._
@import play.api.Play.current
@(handler: DeadboltHandler = handlerCache(current).apply(), name: String, meta: String = null, timeout: Function0[Long] = viewSupport.defaultTimeout)(body: => play.twirl.api.Html)(implicit request: Request[Any])

@if(viewSupport.dynamic(name, meta, handler, timeout(), request)) {
@body
}