Complete unit testing example in play framework + DI - scala

I am looking for complete example of unit test for play 2.4 application + DI.
The idea is very simple:
We have application.test.conf from which I want to read the configuration data, run evolutions etc.
Then I want to inject instance of class which uses DI, example:
class UserBean #Inject()(dbConfigProvider: DatabaseConfigProvider, implicit val configuration: Configuration, cacheApi: CacheApi) {
}
Then call methods of injected object and test it.
The problem that I faced described here: https://stackoverflow.com/questions/37192401/inject-my-bean-like-class-to-test-play-2-4 but nobody answers my question.
Appreciate any help

The Java properties have the highest precedence for loading a conf file with TypeSafe Config. You can tell the sbt to use different config file when running the tests:
javaOptions in Test += "-Dconfig.resource=" + System.getProperty("config.resource", "application.test.conf")
You can create your test Application with GuiceBuilder, see here.
Note that you must have a running app in your test, like:
val myTestApp = new GuiceApplicationBuilder()
.overrides(bind[Component].to[MockComponent])
.build
"my test" in running(myTestApp) { ... }
And then you can use injector, like this:
val app2MyDao = play.api.Application.instanceCache[MyDAO]
val myDAO: MyDAO = app2MyDao(myTestApp)
You can also use the ScalaTest's traits like OneAppPerSuite and override it's fake app.
EDIT: I've made a simple project to demonstrate what I wanted to show.

Related

Testing Scala Play + Slick: why is it picking the wrong database?

I'm working on this github project that uses Play 2.5.10 and Slick 3.1.1 (the issue can be reproduced there by running sbt test but can also be checked directly in travis CI). I use a Postgres database configuration default for development and production. I then use a H2 in memory database called test for testing. The default database is configured in conf/application.conf whereas the test database is configured in conf/application.test.conf.
The problem is that for testing I initialize the database with name test but the Application built with GuiceApplicationBuilder is still picking up the default one.
This line is in my build.sbt to pick up the test configuration:
javaOptions in Test += "-Dconfig.file=conf/application.test.conf"
and this is the content of that file:
include "application.conf"
slick.dbs {
test {
driver="slick.driver.H2Driver$"
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:test;MODE=PostgreSQL"
db.username="sa"
db.password=""
}
}
My DaoFunSpec base class looks like this:
package dao
import org.scalatest.{BeforeAndAfterAll, FunSpec}
import org.scalatestplus.play.OneAppPerSuite
import play.api.Application
import play.api.db.evolutions.Evolutions
import play.api.db.DBApi
abstract class DaoFunSpec extends FunSpec with OneAppPerSuite with BeforeAndAfterAll {
lazy implicit val db = app.injector.instanceOf[DBApi].database("test")
override def beforeAll() {
Evolutions.applyEvolutions(db)
}
override def afterAll() {
Evolutions.cleanupEvolutions(db)
}
def userDao(implicit app: Application) = {
Application.instanceCache[UserDao].apply(app)
}
}
Note the line app.injector.instanceOf[DBApi].database("test") but still Play tries to connect to the default database.
Ok your problem is kinda different (or maybe perhaps a little unexpected). This is the line that causes your headache:
dbApi.databases().foreach(runEvolutions)
Its in: ApplicationEvolutions.scala:42
It's probably self-explanatory :)
Still the problem is more involved. You have two databases actually in your test environment (default and test). Now this leads to several problems - one of which you see above (evolutions are tried on each of them). Another is that if you want to use differently named db you can't just inject something like that:
class UserDao #Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
instead you would need to use (AFAIR):
class UserDao #Inject()(#NamedDatabase("test") protected val dbConfigProvider: DatabaseConfigProvider)
But in that case you testing becomes more complicated.
It would be all much simpler if:
1) You would extract common configuration to common.conf
2) You would change your application.conf to something like this:
include "common.conf"
slick.dbs {
default {
driver="slick.driver.PostgresDriver$"
db.driver="org.postgresql.Driver"
db.url="jdbc:postgresql://localhost:5432/exampledb?searchpath=public"
db.user="postgres"
db.password="postgres"
}
}
3) You would change your application.test.conf to something like this:
include "common.conf"
slick.dbs {
default {
driver="slick.driver.H2Driver$"
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:test;MODE=PostgreSQL"
db.username="sa"
db.password=""
}
}
Now the only thing is that you should rather have one set of evolutions (default) which is actually not that bad as it would make sure your test db is in sync with your production db (at least in terms of structure).
It's not that above is the only solution. You could still have two differently named db configurations; you would need in such scenario to do some kind of remapping in your Guilce module (you would then have one module for prod and one for test - they could inherit from one another and only overwrite certain thing - like e.g. attaching test db in place of default one). It's basically a matter of taste.

Scala and Slick: DatabaseConfigProvider in standalone application

I have an Play 2.5.3 application which uses Slick for reading an object from DB.
The service classes are built in the following way:
class SomeModelRepo #Inject()(protected val dbConfigProvider: DatabaseConfigProvider) {
val dbConfig = dbConfigProvider.get[JdbcProfile]
import dbConfig.driver.api._
val db = dbConfig.db
...
Now I need some standalone Scala scripts to perform some operations in the background. I need to connect to the DB within them and I would like to reuse my existing service classes to read objects from DB.
To instantiate a SomeModelRepo class' object I need to pass some DatabaseConfigProvider as a parameter. I tried to run:
object SomeParser extends App {
object testDbProvider extends DatabaseConfigProvider {
def get[P <: BasicProfile]: DatabaseConfig[P] = {
DatabaseConfigProvider.get("default")(Play.current)
}
}
...
val someRepo = new SomeModelRepo(testDbProvider)
however I have an error: "There is no started application" in the line with "(Play.current)". Moreover the method current in object Play is deprecated and should be replaced with DI.
Is there any way to initialize my SomeModelRepo class' object within the standalone object SomeParser?
Best regards
When you start your Play application, the PlaySlick module handles the Slick configurations for you. With it you have two choices:
inject DatabaseConfigProvider and get the driver from there, or
do a global lookup via DatabaseConfigProvider.get[JdbcProfile](Play.current), which is not preferred.
Either way, you must have your Play app running! Since this is not the case with your standalone scripts you get the error: "There is no started application".
So, you will have to use Slick's default approach, by instantiating db directly from config:
val db = Database.forConfig("default")
You have lot's of examples at Lightbend's templates.
EDIT: Sorry, I didn't read the whole question. Do you really need to have it as another application? You can run your background operations when your app starts, like here. In this example, InitialData class is instantiated as eager singleton, so it's insert() method is run immediately when app starts.

ScalaWS in play framework 2.5 and dependency injection of WSClient in custom class

I'm using Scala Play framework 2.5 and I would like to use dependency injection to inject an instance of WSClient into my custom class but I keep getting the following error.
not enough arguments for constructor TestClass: (ws: play.api.libs.ws.WSClient)service.TestClass. Unspecified value parameter ws.
I get the error when running the following code
class TestClass #Inject() (ws: WSClient) {
def doSomething() : Future[WSResponse] = {
ws.url("http://www.google.com").get()
}
}
val test = new TestClass()
val f = test.doSomething()
val result = Await.result(f, Duration.Inf)
println("doSomething: " + result)
Can someone help me resolve this problem of trying to inject a wsclient dependency into a custom class?
Thanking you in advance
Francis
This line:
val test = new TestClass()
It is not using the dependency injection support provided by Play. You are manually creating the instance of TestClass. I truly recommend that you read the following doc page:
PlayFramework: Scala Dependency Injection
Basically, when using runtime Dependency Injection, you don't create the instances manually. You let the DI framework do the job for you. But, if you are instead interested in compile time dependency injection, see the following page:
PlayFramework: Compile Time Dependency Injection

akka-http with multiple route configurations

Quick Background
I am running through some examples learning the Akka HTTP stack for creating a new REST project (completely non-UI). I have been using and augmenting the Akka HTTP Microservice Example to work through a bunch of use cases and configurations and have been pleasantly surprised by how well Scala & Akka HTTP work.
Current Setup
Currently I have a configuration like this:
object AkkaHttpMicroservice extends App with Service {
override implicit val system = ActorSystem()
override implicit val executor = system.dispatcher
override implicit val materializer = ActorMaterializer()
override val config = ConfigFactory.load()
override val logger = Logging(system, getClass)
Http().bindAndHandle(routes, config.getString("http.interface"), config.getInt("http.port"))
}
The routes parameter is just a simple value that has the typical data within it using path, pathPrefix, etc.
The Problem
Is there any way to set up routing in multiple Scala files or an example somewhere out there?
I would really like to be able to define a set of classes that separate the concerns and deal with Actor setup and processing to deal with different areas of the application and just leave the marshaling to the root App extension.
This might be me thinking too much in terms of how I did things in Java using annotations like #javax.ws.rs.Path("/whatever") on my classes. If that is the case, please feel free to point out the change in mindset.
I tried searching for a few different set of keywords but believe I am asking the wrong question (eg, 1, 2).
Problem 1 - combine routes in multiple files
You can combine routes from multiple files quite easy.
FooRouter.scala
object FooRouter {
val route = path("foo") {
complete {
Ok -> "foo"
}
}
}
BarRouter.scala
object BarRouter {
val route = path("bar") {
complete {
Ok -> "bar"
}
}
}
MainRouter.scala
import FooRouter
import BarRouter
import akka.http.scaladsl.server.Directives._
import ...
object MainRouter {
val routes = FooRouter.route ~ BarRouter.route
}
object AkkaHttpMicroservice extends App with Service {
...
Http().bindAndHandle(MainRouter.routes, config.getString("http.interface"), config.getInt("http.port"))
}
Here you have have some docs :
http://doc.akka.io/docs/akka-http/current/scala/http/routing-dsl/overview.html
http://doc.akka.io/docs/akka-http/current/scala/http/routing-dsl/routes.html
Problem 2 - seprate routing, marshalling, etc
Yes, you can separate routing, marshalling and application logic. Here you have activator example: https://github.com/theiterators/reactive-microservices
Problem 3 - handle routes using annotations
I don't know any lib that allow you to use annotion to define routing in akka-http. Try to learn more about DSL routing. This represents a different approach to http routing but it is convenient tool too.

Play-ReactiveMongo plugin: play2 testing a controller with a reactiveMongoApi

In the following, I am using the Play2 ReactiveMongo plugin in version 0.11.0.play24 (https://github.com/ReactiveMongo/Play-ReactiveMongo)
for Play 2.4.
As mentioned in the documentation located at http://reactivemongo.org/releases/0.11/documentation/tutorial/play2.html, a Play2 controller with Mongo is instantiated as follows:
class MyController #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents { }
Therefore, since the controller is now a class and not an object, it is not possible to use it as a singleton in the test cases.
However, I do not know how to inject the reactiveMongoApi in order to instantiate a MyController() with the right parameters in a test case (ScalaCheck or other...)
Do you have any idea/example on how to test such a controller with ScalaCheck or Specs2?
Thank you in advance!
You can produce a mock for ReactiveMongoApi (depending which mock framework you use):
val reactiveMongoApi = mock[ReactiveMongoApi]
and then you can do this:
new MyController(reactiveMongoApi)
That's the simplest approach. To use the actual ReactiveMongoApi object:
val app = new GuiceApplicationBuilder()
.in(Mode.Test)
.configure("play.modules.enabled" -> "play.modules.reactivemongo.ReactiveMongoModule")
.build
val reactiveMongoApi = app.injector.instanceOf[ReactiveMongoApi]
If it gets more complicated, for example, partially mocked nested dependency tree (this is more integration testing than unit testing), you may want to partially mock the Guice framework as explained here.
This project use Guice for dependency injection, Spec2 for testing controllers, and Frisby for testing endpoints.
https://github.com/luongbalinh/play-mongo