Play framework GuiceApplicationBuilder not loading enabled module configs - scala

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.

Related

Play 2.5 Guice errors when compiling

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=

Injecting Play Application for a Slick Database Service

This is regarding a Play 2.3 application that I am trying to introduce Guice into.
I have the following legacy code to Guicify and I reached this point
class DatabaseService {
def db: Database = DB(play.api.Play.current)
//More code below.
}
This class is injected in the class under test, say com.TestA.
When I run the test cases using a modified OneAppPerTest setup, I see ProvisionExceptions of the following kind.
Error injecting constructor, java.lang.RuntimeException: There is no started application
Is it because of the use of play.api.Play.current?
How do I fix this issue?
Additional Info
The overridden OneAppPerTest variant, uses the following logic
override def newAppForTest(testData: TestData): FakeApplication = {
new FakeApplication(
additionalConfiguration = additionalConfiguration,
withGlobal = Some(globalSettings)
)
}
where additionalConfiguration can be overridden by actual tests and globalSettings is where Guice is introduced within the framework
protected def globalSettings = {
new TestGlobalSettings {
val injector = Guice.createInjector(overriddenModules: _*)
override def getControllerInstance[A](controllerClass: Class[A]): A = {
injector.getInstance(controllerClass)
}
override def getInjector = {
injector
}
}
}
There are other test cases with a similar setup that work perfectly fine. I am able to inject mocks, test the classes. However, every test with this DatabaseService class is failing, because I can't inject it cleanly.
The issue is related to how Guice creates the objects and when. DatabaseService here had access to the current application, the current configuration. However, in my Global.scala, I had it injected before 'onStart' was actually called.
This implies that we are trying to access an application before it was properly initialized.
Making DatabaseService injection in the test class lazy helped avoid the error.

Play Slick: How to inject DbConfigProvider in tests

I am using Play 2.5.10, Play-slick 2.0.2, and my activator-generated project comes with scalatest and code like this:
class TestSpec extends PlaySpec with OneAppPerSuite {...}
I managed to test routes/Actions; now I would test DAO methods on a lower level. I searched the web and SO for a solution, and could not find any that is still up-to-date. A DAO signature is like this:
class TestDAO #Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile]
so I need to pass it the dbConfigProvider thing.
For some reason I can't inject the provider into the tests like we do in controllers (no error, tests just won't run):
class TestSpec #Inject()(dbConfigProvider: DatabaseConfigProvider) extends PlaySpec with OneAppPerSuite {...}
The Play-Slick docs say we can alternatively use a global lookup
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
but it won't work directly because
There is no started application
and link to an example project doing that:
class TestDAOSpec extends Specification {
"TestDAO" should {
"work as expected" in new WithApplicationLoader { // implicit 'app'
val app2dao = Application.instanceCache[TestDAO].apply(app)
but I could never find the WithApplicationLoader. Instead, there seems to be a WithApplication:
class TestDAOSpec extends Specification {
"TestDAO" should {
"work as expected" in new WithApplication() { // implicit 'app'
val app2dao = Application.instanceCache[TestDAO].apply(app)
but then I get
Type mismatch: expected a play.api.Application, got: play.Application.
At this point I lost hope.
How can I test a DAO?
N.B. I don't need to switch databases for testing (I handle this via config), I just want to access the default database in tests.
You can use:
lazy val appBuilder: GuiceApplicationBuilder = new GuiceApplicationBuilder().in(Mode.Test)
lazy val injector: Injector = appBuilder.injector()
lazy val dbConfProvider: DatabaseConfigProvider = injector.instanceOf[DatabaseConfigProvider]

Scala Play Guice using injector to manually inject a singleton class that has configuration injected into its constructor

using play 2.5 and guice i have managed to successfully inject applicationConfig into a singleton class and reference a config variable inside it,
trait TMongoFactory{
val SERVER: String
val PORT: Int
val DATABASE: String
val connection: MongoClient
val collection: MongoDB
}
#Singleton
class MongoFactory #Inject()(val configuration: Configuration) extends TMongoFactory{
val SERVER = "localhost"
val PORT = 27017
val DATABASE = configuration.underlying.getString("connectionString")
val connection = MongoClient(SERVER, PORT)
val collection = connection(DATABASE)
}
class MongoModule extends AbstractModule {
def configure() = {
bind(classOf[TMongoFactory]).to(classOf[MongoFactory])
}
}
I can then pass this singleton to a repository class like so
#Singleton
class MongoRemainingAllowanceRepository #Inject()(MongoFactory: TMongoFactory) extends RemainingAllowanceRepository{
val context = MongoFactory.collection("remainingAllowance")
def save(remainingAllowance: RemainingAllowance): Unit ={
context.save(RemainingAllowance.convertToMongoObject(remainingAllowance))
}
This all works fine and as expected, but the problem is i need to call this repository in the test suite so i dont want it to have to take any arguments (specifically injected ones).
So i tried to change it to use an injector inside the body like so
#Singleton
class MongoRemainingAllowanceRepository extends RemainingAllowanceRepository{
val injector = Guice.createInjector(new MongoModule)
val mongoFactory = injector.getInstance(classOf[TMongoFactory])
val context = mongoFactory.collection("remainingAllowance")
def save(remainingAllowance: RemainingAllowance): Unit ={
context.save(RemainingAllowance.convertToMongoObject(remainingAllowance))
}
This feels like it should work and it compiles fine, but then on test or run it throws an error
Could not find a suitable constructor in play.api.Configuration. Classes
must have either one (and only one) constructor annotated with #Inject
or a zero-argument constructor that is not private. at
play.api.Configuration.class(Configuration.scala:173) while locating
play.api.Configuration
Apologies for the long post but i feel i needed to include most of this.
Does anyone know why this happens on an injector? Do i need to bind the configuration manually also now im referencing the custom module?
Any help appreciated
Thanks
Jack
When you create your class you can pass in the configuration yourself. Say you need key apiKey and its value...
val sampleConfig = Map("apiKey" ->"abcd1234")
val mongoFactory = new MongoFactory(Configuration.from(sampleConfig))

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