Search code examples
scalaplayframework

Play Framework Unit Testing Controllers With Compile Time DI


I have the following Controller in Play Framework:

final class ProvisioningController(bindings: Bindings[IO],
  val controllerComponents: ControllerComponents) extends ControllerBase {
  ......
  ......
  ......
  ......
}

As you can see I'm using a class called Bindings that contains all the necessary dependencies. I'm not doing any @Inject, but rather managing them by myself through Bindings. Now, I would like to unit test my Controller, so I wanted to use the Play Framework scala test and I have my controller spec like this:

class ProvisioningControllerSpec extends ControllerBaseSpec with Results {

  implicit val timeout: Timeout = 5.seconds // Change the duration as needed

    "ProvisioningController#appConfig" should {
    "should return the active AppConfig as JSON" in {
      val controller             = new ProvisioningController(bindings.appConfig, Helpers.stubControllerComponents())
      val result: Future[Result] = controller.appConfig.apply(FakeRequest())
      val bodyText: JsValue      = contentAsJson(result)
      bodyText mustBe "ok"
    }
  }
}

When I ran my application, it ran into an error that I could now understand:

[info] 1) No implementation for controllers.impl.ProvisioningController (with no qualifier annotation) was bound, and could not find an injectable constructor. Injectable classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
[info]   at controllers.impl.ProvisioningController.class(ProvisioningController.scala:16)
[info]   while locating controllers.impl.ProvisioningController
[info]     for the 3rd parameter of router.Routes.<init>(Routes.scala:28)
[info]   at play.api.inject.RoutesProvider$.bindingsFromConfiguration(BuiltinModule.scala:139):
[info] Binding(class router.Routes to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$4)

How can I fix this?


Solution

  • I had to instantiate my own FakeApplication by extending the FakeApplicationFactory class like this:

      override def fakeApplication(): Application = {
        val env = Environment.simple(new File("."))
        val context = ApplicationLoader.Context.create(
          environment = env,
          initialSettings = Map.empty,
          lifecycle = new DefaultApplicationLifecycle()
        )
        new Bootstrap().load(context)
      }