Play 2.5 Guice errors when compiling - scala

I am currently in the process of migrating my app from Play 2.4 to Play 2.5 So far, it's been a massive pain.
Right now, I am trying to make my tests to pass.
I have one controller with some injected params
class UserLogin #Inject() (
loginService: UserLoginService,
authConfig: AuthConfig,
oAuthConfig: OAuthConfig,
env: PureEnvironment,
//stringResources: StringResources,
messagesApi: MessagesApi
) extends BaseController(messagesApi: MessagesApi) {
//endpoints
}
From the testing side, I have them defined to be injected with Guice
object IdentityManagerModuleMock extends AbstractModule with ScalaModule {
def configure: Unit = {
bind[PureEnvironment].toInstance(MockEnvironment)
val conf = Configuration.reference
val messagesApi = new DefaultMessagesApi(Environment.simple(), conf, new DefaultLangs(conf))
bind[MessagesApi].toInstance(messagesApi)
bind[AuthConfig].toInstance(
AuthConfig(
"https://localhost",
30,
3600,
86400,
Seq(InetAddress.getByName("127.0.0.1")),
Seq(InetAddress.getByName("127.0.0.1")),
"https://localhost/login"
)
)
bind[OAuthConfig].toInstance(oAuthConfigMock)
bind[UserLoginService].to[UserLoginServiceImpl]
}
val injector = Guice.createInjector(IdentityManagerModuleMock)
When I enable the routes in my routes file,
POST /login com.dummy.im.controllers.UserLogin.login
POST /reset com.dummy.im.controllers.UserLogin.reset
1) No implementation for com.dummy.im.components.UserLoginService was bound.
[info] while locating com.dummy.im.components.UserLoginService
[info] for parameter 0 at com.dummy.im.controllers.UserLogin.<init>(UserLogin.scala:24)
2) No implementation for com.dummy.platform.PureEnvironment was bound.
[info] while locating com.dummy.platform.PureEnvironment
[info] for parameter 3 at com.dummy.im.controllers.UserLogin.<init>(UserLogin.scala:24)
3) No implementation for scala.collection.Seq<java.net.InetAddress> was bound.
[info] while locating scala.collection.Seq<java.net.InetAddress>
[info] for parameter 4 at com.dummy.im.config.AuthConfig.<init>(AuthConfig.scala:13)
5) Could not find a suitable constructor in java.lang.Integer. Classes must have either one
(and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
[info] at java.lang.Integer.class(Integer.java:52)
[info] while locating java.lang.Integer
[info] for parameter 1 at com.dummy.im.config.AuthConfig.<init>(AuthConfig.scala:13)
The weird thing is that I have a very similar config for actual running the app, without the MessagesAPI (injected by Play, as far as I know) and those config objects which are read from the .conf file.
object IdentityManagerModule extends AbstractModule with ScalaModule {
def configure: Unit = {
bind[PureEnvironment].to[PlayProductionEnvironmentImpl]
bind[OauthRepository].to[OauthRepositoryImpl]
bind[UserLoginService].to[UserLoginServiceImpl]
}
}
And it runs just fine.
The main thing that I changed was to add the dependency to MessagesAPI in the controller.
I don't understand why Guice fails to see the things that are binded in IdentityManagerModuleMock.
Any ideas are more than welcomed. I have read and tried all I could think of for the past several days.
EDIT:
We have a custom app loader
class CustomApplicationLoader extends GuiceApplicationLoader() {
//config validation
override def builder(context: ApplicationLoader.Context): GuiceApplicationBuilder = {
val conf = context.initialConfiguration
initialBuilder
.in(context.environment)
.loadConfig(conf)
.overrides(overrides(context): _*)
.bindings(
MiscModule,
CommonConfigurationModule,
IdentityManagerConfigModule,
IdentityManagerModule,
ApplicationLifecycleModule
)
}
}
In the .conf file, it's used as
play.application.loader = "com.dummy.guice.CustomApplicationLoader"

It doesn't look like you need a custom application loader for what you're doing. If you disable the out of the box MessagesApi and then add your own modules through application.conf, that should mean you don't have to override MessagesApi.
https://www.playframework.com/documentation/2.5.x/ScalaPlayModules#Registration-Overrides
If you're running tests involving Guice, you can use WithApplication.
https://www.playframework.com/documentation/2.5.x/ScalaFunctionalTestingWithSpecs2#WithApplication
You shouldn't ever have to call Guice.createInjector directly, because WithApplication will call out to GuiceApplicationBuilder:
https://www.playframework.com/documentation/2.5.x/ScalaTestingWithGuice#GuiceApplicationBuilder
Check out the example projects: they all have "2.5.x" branches and most have tests integrated into them, and leverage them as a guide:
https://github.com/playframework?utf8=%E2%9C%93&q=examples&type=&language=

Related

Play framework GuiceApplicationBuilder not loading enabled module configs

I'm trying to test some actions. They require my security stuff which I've created using Silhouette.
I have a module to configure the DI for Silhouette in security.Module, and in my conf/application.conf file I have the line:
play.modules.enabled += "security.Module"
When I run my tests I get an error saying:
No implementation for com.mohiva.play.silhouette.api.Silhouette<security.JwtEnv> was bound.
If I set a breakpoint in my security.Module class, it's never triggered. However if I set a breakpoint in my main Module class it is. So I can see that my security.Module is never being loaded despite the docs for GuiceApplicationBuilder saying enabled modules are loaded automatically.
Here's how I'm configuring GuiceApplicationBuilder in my test class (based on scalatest):
class TestUserController extends PlaySpec with OneAppPerTest with Results {
val identity = UserDao(
id = Some(10),
email = Some("testuser#example.com"),
createdAt = DateTime.parse("2016-11-11T11:11:11")
)
val loginInfo = LoginInfo("credentials", identity.email.get)
implicit val env = FakeEnvironment[JwtEnv](Seq(loginInfo -> identity))
implicit override def newAppForTest(td: TestData) = new GuiceApplicationBuilder()
.overrides(bind[UsersBlockedRepo].to[MockUsersBlockedRepo])
.build
//...
}
What am I missing to get GuiceApplicationBuilder to load my security module?
I've fixed this. The problem was that my security.Module took constructor args:
class Module(environment: PlayEnvironment,
configuration: Configuration) extends AbstractModule with ScalaModule {...}
Removing those fixed it.

Intergation testing with play 2.5.4 and DI - play configuration won't load

I want to integration test an app by spinning up an in memory database. I had thought this might not be too tricky with the dependency injection:
class TreatySpec extends PlaySpec with OneAppPerSuite with Results {
val testConf = Configuration(ConfigFactory.load("test.conf"))
override lazy val app = new GuiceApplicationBuilder().in(Mode.Test).loadConfig(testConf).bindings(new TestModule()).build()
"Routes" should {
"404 on a bad request" in {
route(app, FakeRequest(GET, "/boum")).map(status(_)) mustBe Some(NOT_FOUND)
}
}
}
When executed, I get the following error:
[info] TreatySpec:
[info] Exception encountered when attempting to run a suite with class name: TreatySpec *** ABORTED ***
[info] com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'slick.dbs.h2'
Here is my test.conf:
slick.dbs.h2.driver="slick.driver.H2Driver$"
slick.dbs.h2.db.driver="org.h2.Driver"
slick.dbs.h2.db.url="jdbc:h2:mem:test"
slick.dbs.h2.db.username=""
slick.dbs.h2.db.password=""
slick.dbs.h2 {
driver="slick.driver.H2Driver$"
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:spotRepo"
db.username=""
db.password=""
}
slick.dbs{
h2.driver="slick.driver.H2Driver$"
h2.driver="org.h2.Driver"
h2.url="jdbc:h2:mem:spotRepo"
h2.username=""
h2.password=""
}
I thought the Guice module was supposed to load the configuration? Whatever I do, there doesn't appear to be the relevant key in the config.
class TestModule extends AbstractModule {
override def configure() = {
// Use the system clock as the default implementation of Clock
bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)
}
val dbConfigString = "slick.dbs.h2"
#Provides
val dbConfig : DatabaseConfig[JdbcProfile] = DatabaseConfig.forConfig[JdbcProfile](dbConfigString)
}
Can anyone point me in the right direction here? What am I doing wrong such that play refuses to load the configuration?

Play with scala - Injecting dependencies through an instance of Guice Injector

My application makes use of Play-2.4.2/Scala-2.11.6 that comes with built-in Guice support for DI
All my DAOs bind an implementation to an interface as below , which is supposed to be the simplest way in Guice
#ImplementedBy(classOf[PersonDAOImpl])
trait PersonDAO {
}
class PersonDAOImpl #Inject()
(
(#NamedDatabase("mysql")protected val dbConfigProvider: DatabaseConfigProvider,
protected val cache : CacheApi) extends PersonDAO with SQLWrapper {
..
...
}
The above implementation does not need addition of any module to provide bindings.
Now for some reason, I do not want to inject the dependencies into the constructor of Books class using #Inject annotation . So , I tried injecting it as below
class Books {
val injector = Guice.createInjector()
val personDAO : PersonDAO = injector.getInstance(classOf[PersonDAOImpl])
..
...
}
But this throws me a guice configuration exception saying :
Caused by: com.google.inject.ConfigurationException: Guice configuration errors:
1) No implementation for play.api.cache.CacheApi was bound.
while locating play.api.cache.CacheApi
for parameter 1 at schema.PersonDAOImpl.<init>
while locating PersonDAO
2) No implementation for play.api.db.slick.DatabaseConfigProvider annotated with #play.db.NamedDatabase(value=mysql) was bound.
while locating play.api.cache.CacheApi
for parameter 2 at schema.PersonDAOImpl.<init> while locating PersonDAO
What needs to be done now ? Is my approach right or wrong in this case ? Can someone help me out with this ? Thanks in advance.
You can use the Injector from the current Play Application.
import play.api.{ Application, Play }
import play.api.inject.Injector
val currentApp: Application = Play.current
val injector: Injector = currentApp.injector
// in short play.api.Play.current.injector
// Then use the injector
import play.api.inject.ApplicationLifecycle
current.injector.instanceOf[ApplicationLifecycle].
addStopHook { () => ??? }
(See example using injector with the Play plugin for ReactiveMongo)

Scala Play application integration tests with guice context

I'm new to play framework and have limited experience with scala. Using play framework 2.4 I'm trying to write full integrations tests where I want to call controller, and using in-memory db retrieve data from it. I'm using scalatest-plus together with scalatest. I have read about play unit tests in play documentation and now trying to use play.api.test.FakeApplication but i cannot find a way to inject my guice module to this fake application. I'm ussing OneAppPerSuite trait I can override FakeApplication but cannot pass any guice module for injection. Here is FakeApplication source from:
case class FakeApplication(
override val path: java.io.File = new java.io.File("."),
override val classloader: ClassLoader = classOf[FakeApplication].getClassLoader,
additionalPlugins: Seq[String] = Nil,
withoutPlugins: Seq[String] = Nil,
additionalConfiguration: Map[String, _ <: Any] = Map.empty,
withGlobal: Option[play.api.GlobalSettings] = None,
withRoutes: PartialFunction[(String, String), Handler] = PartialFunction.empty) extends Application {
private val app: Application = new GuiceApplicationBuilder()
.in(Environment(path, classloader, Mode.Test))
.global(withGlobal.orNull)
.configure(additionalConfiguration)
.bindings(
bind[FakePluginsConfig] to FakePluginsConfig(additionalPlugins, withoutPlugins),
bind[FakeRouterConfig] to FakeRouterConfig(withRoutes))
.overrides(
bind[Plugins].toProvider[FakePluginsProvider],
bind[Router].toProvider[FakeRouterProvider])
.build
....
So there is no way for me to pass custom module here.
Here is how my test looks like:
class UsersControllerTest extends PlaySpec with OneAppPerSuite {
override implicit lazy val app = FakeApplication(additionalConfiguration = inMemoryDatabase())
"Application " should {
"work" in {
val resp = route(FakeRequest(GET, "/api/users")).get
}
}
}
And here is my controller I want to test:
class UserController #Inject()
(userService: UserService)
(implicit ec: ExecutionContext) extends Controller {
def get = Action.async { implicit request => {
// implementation
}
}
}
Obviously my test now fail since it cannot find UserService instance.
So my question is:
How to properly write end2end integration tests with play using guice context
Is it common to write such tests in play application, since I cannot find any decent examples or documentation of doing something like that. I used to write such tests in java web applications where integration tests load spring context, but can't find such examples in play.
Thanks in advance.
If you want to test your default configuration, starting FakeApplication, is enough. It loads modules specified in application.conf automatically. So, If it can't find UserService, that means you don't have proper module configurations in your application.conf and there for your server can't serve those requests.
https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection
If you want to use some Mockups, and override default configurations:
https://www.playframework.com/documentation/2.4.x/ScalaTestingWithGuice

Pool has been Shutdown (HikariDataSource) when testing

After migrating an application to Play 2.4 and introducing dependency injection into the controller of the application, I'm getting "Pool has been Shutdown" when running unit tests. The affected tests are something like this:
#RunWith(classOf[JUnitRunner])
class ApplicationSpec extends Specification {
"Application" should {
"doSomething" in running(TestUtil.app) {
val myId = IdGen.newId("someone")
...
}
}
}
Where the IdGen class looks something like:
object IdGen {
def newId(name: String): ClientCredentials = {
DB.withTransaction("myDb") { implicit conn =>
...
}
}
}
The test fails on the DB.withTransaction() call with
[error] Pool has been shutdown (HikariDataSource.java:89)
[error] com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:89)
[error] play.api.db.DefaultDatabase.getConnection(Databases.scala:143)
[error] play.api.db.DefaultDatabase.withConnection(Databases.scala:153)
[error] play.api.db.DefaultDatabase.withTransaction(Databases.scala:162)
[error] play.api.db.DB$.withTransaction(DB.scala:72)
[error] com.example.idGen$.newId...
I'm initializing TestUtil.app with
object TestUtil {
lazy val app = new GuiceApplicationBuilder()
.configure(defaultConfig ++ Helpers.inMemoryDatabase("myDB"))
.bindings(new TestModule) // Mock injections for test
.build
}
Clearly I'm missing something to get the database up and running for tests, but I'm unsure what.
Solved this.
Tried to replace lazy val with def, as answered in this question: Testing: FakeApplication ignoring additionalConfiguration
Which solved the problem.
Would love if someone could explain why?
This problem bit me because I was holding on to Application state in the code. Since tests typically each get their own FakeApplication, squireled-away state from one FakeApplication instance, when used in the context of another FakeApplication instance, will lead to problems. A concrete example is play.api.Play.current - this needs to be re-evaluated for each FakeApplication instance and shouldn't be held (after being evaluated only once) in your code.