2

I'm looking for a way to inject a dependency into a Test (in /tests/models/) that looks like following:

class FolderSpec(implicit inj: Injector) extends Specification with Injectable{

  val folderDAO = inject [FolderDAO]

  val user = User(Option(1), LoginInfo("key", "value"), None, None)

  "Folder model" should {

    "be addable to the database" in new WithFakeApplication {
      folderDAO.createRootForUser(user)
      val rootFolder = folderDAO.findUserFolderTree(user)
      rootFolder must beSome[Folder].await
    }

  }
}

Where

abstract class WithFakeApplication extends WithApplication(FakeApplication(additionalConfiguration = inMemoryDatabase()))

/app/modules/WebModule:

class WebModule extends Module{
  bind[FolderDAO] to new FolderDAO
}

/app/Global:

object Global extends GlobalSettings with ScaldiSupport with SecuredSettings with Logger {
  def applicationModule = new WebModule :: new ControllerInjector
}

But at compilation time I have following stack trace:

[error] Could not create an instance of models.FolderSpec
[error]   caused by java.lang.Exception: Could not instantiate class models.FolderSpec: argument type mismatch
[error]   org.specs2.reflect.Classes$class.tryToCreateObjectEither(Classes.scala:93)
[error]   org.specs2.reflect.Classes$.tryToCreateObjectEither(Classes.scala:207)
[error]   org.specs2.specification.SpecificationStructure$$anonfun$createSpecificationEither$2.apply(BaseSpecification.scala:119)
[error]   org.specs2.specification.SpecificationStructure$$anonfun$createSpecificationEither$2.apply(BaseSpecification.scala:119)
[error]   scala.Option.getOrElse(Option.scala:120)

Sadly, I didn't find anything on the matter in Scaldi documentation.

Is there a way to inject things in tests?

Mironor
  • 1,157
  • 10
  • 25

1 Answers1

1

Scaldi does not provide an integration with any testing framework, but you actually normally don't need it. What you can do in this case is to create a test Module that contains mocks and stubs (like in-memory databases) and then just provide a test Global to the FakeApplication. Here is an example of how you can do it:

"render the index page" in {
  class TestModule extends Module {
    bind [MessageService] to new MessageService {
      def getGreetMessage(name: String) = "Test Message"
    }
  }

  object TestGlobal extends GlobalSettings with ScaldiSupport {

    // test module will override `MessageService`
    def applicationModule = new TestModule :: new WebModule :: new UserModule
  }

  running(FakeApplication(withGlobal = Some(TestGlobal))) {
    val home = route(FakeRequest(GET, "/")).get

    status(home) must equalTo(OK)
    contentType(home) must beSome.which(_ == "text/html")

    println(contentAsString(home))

    contentAsString(home) must contain ("Test Message")
  }
}

You can find this code in the scaldi-play-example application.

tenshi
  • 26,268
  • 8
  • 76
  • 90
  • That's what I saw in your scaldi-play-example repo, but I didn't find how to explicitly inject things in test class itself. I'm talking about this line "val folderDAO = inject [FolderDAO]" in my example. How can it be done? Do you have any plans for tests integration in Play framework? – Mironor Oct 22 '14 at 05:05
  • Or maybe it's a bad idea and I shouldn't do explicit injections in test classes anyway? – Mironor Oct 22 '14 at 16:27
  • @Mironor that's true. When I think about it, if test itself is injected, than you need some kind of global test module where all tests are bound, or at least there should be some global injector available for all tests. This would mean that you need to define all possible mocks for all tests in it. In most cases is simply not practical. I would recommend to keep the scope of test module smaller, like in example I showed - it defines a module for this particular test case. but you can also do it for the whole test class. – tenshi Oct 22 '14 at 18:37
  • After you defined a test module, you can can also make it implicitly available for the test like this: `implicit val injector = TestGlobal.applicationModule` and then inject things from it like in your example: `val folderDAO = inject [FolderDAO]` (in this case you need to either `import Injectable._` or just extend it in the test class) – tenshi Oct 22 '14 at 18:41
  • @Mironor sorry, there is a mistake in the comment above. instead of `implicit val injector = TestGlobal.applicationModule` t should be `implicit val injector = TestGlobal.injector`. The reason for this is explained here: https://groups.google.com/d/msg/scaldi/SQibDMWO9KE/FZmVaZ-1xAoJ – tenshi Nov 12 '14 at 22:35