dlite922
dlite922

Reputation: 1994

How to bind dependency injected system wrapper to play application controller?

My Application depends on a custom class that inherits from trait SystemComponent.

package com.example 
trait SystemWrapper { ... }
trait MySystemWrapper extends SystemWrapper { ... } 

class RealSystemWrapper extends MySystemWrapper { dao: RealDAOFactory =>
  val blah = new MySystem("Production")
}

in my Application.scala injecting RealSystemWrapper works and compiles just fine but doesn't help me with testing:

class Application @Inject() (syswrap: RealSystemWrapper) extends Controller
{ 
 ...// use syswrap here
}

I would like to use a MockedSystemWrapper extends SystemWrapper with a mocked DAO factory, etc etc. Therefore I would like to inject the trait SystemWrapper like so

class Application @Inject() (syswrap: SystemWrapper) extends Controller 
{ 

but I get ProvisionException: No implementation was bound

1) No implementation for com.example.SystemWrapper was bound.
  while locating com.example.SystemComponent
    for parameter 0 at com.example.controllers.Application.<init>(Application.scala:25)
  while locating com.example.controllers.Application
    for parameter 1 at router.Routes.<init>(Routes.scala:27)
  while locating router.Routes
  while locating play.api.inject.RoutesProvider
  while locating play.api.routing.Router
    for parameter 0 at play.api.http.JavaCompatibleHttpRequestHandler.<init>(HttpRequestHandler.scala:200)
  while locating play.api.http.JavaCompatibleHttpRequestHandler
  while locating play.api.http.HttpRequestHandler
    for parameter 4 at play.api.DefaultApplication.<init>(Application.scala:221)
  at play.api.DefaultApplication.class(Application.scala:221)
  while locating play.api.DefaultApplication
  while locating play.api.Application

Play docs suggest I use @ImplementedBy[classOf[RealSystemWrapper]] decorator but that leaves me with the testing question: how do I change that at testing to classOf[MockedSystemWrapper]?

Upvotes: 0

Views: 385

Answers (1)

rethab
rethab

Reputation: 8403

As Application depends on a trait, you need to define the "binding" (ie. which concrete class to use). One approach is, as you described, the @ImplementedBy annotation. Another approach would be to define the binding in a module as described here: https://www.playframework.com/documentation/2.5.x/ScalaDependencyInjection#Programmatic-bindings

Now if you want to use a different instance in your test, that depends on your test setup. In the simplest case, you're instantiating Application directly in your test and therefore you can pass in the mock by hand. If you're running the entire application you class, then you can override the bindings (as described here: https://www.playframework.com/documentation/2.5.x/ScalaTestingWithGuice#Override-bindings) or pass the overridden modules to the WithApplication helper (as shown here: https://www.playframework.com/documentation/2.5.x/ScalaFunctionalTestingWithSpecs2)

Upvotes: 2

Related Questions