I want to write functional test for my controller in PlayFramework. To do that I want to mock implementation of some classes.
I found nice example of how to do that using spec2 here: http://www.innovaedge.com/2015/07/01/how-to-use-mocks-in-injected-objects-with-guiceplayscala/
But I'm using scala test with OneAppPerSuite trait that uses FakeApplication. Here are documentation:
https://www.playframework.com/documentation/2.4.x/ScalaFunctionalTestingWithScalaTest
Problem is that i cannot found a way to intercept into GuiceApplicationBuilder and override some bindings with mock implementation.
Here are FakeApplication implementation from play.api.test:
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 intercept into GuiceApplicationBuilder and override bindings.
I'm new to playframework so sorry if question looks a bit silly.
Thanks!
Take a look at GuiceOneAppPerTest. Here is an example (Play 2.8, scala 2.13):
abstract class MyBaseSpec extends PlaySpec with GuiceOneAppPerTest with Results with Matchers with MockFactory {
def overrideModules: Seq[GuiceableModule] = Nil
override def newAppForTest(testData: TestData): Application = {
GuiceApplicationBuilder()
.overrides(bind[ControllerComponents].toInstance(Helpers.stubControllerComponents()))
.overrides(overrideModules: _*)
.build()
}
}
class OrderServiceSpec extends MyBaseSpec {
val ordersService: OrdersService = mock[OrdersService]
val usersService: UsersService = mock[UsersService]
override def overrideModules = Seq(
bind[OrdersService].toInstance(ordersService),
bind[UsersService].toInstance(usersService),
)
// tests
}
You are probably using an older version of ScalaTestPlus, which didn't support overriding FakeApplication with Application. In Play docs(Play 2.4) the library version is "1.4.0-M3" but it should be "1.4.0".
Related
My service is running fine until I hit api endpoint request with this error-
Cannot load play.application.loader[play.application.loader [class java.lang.Class}] does not implement interface play.api.ApplicationLoader or interface play.ApplicationLoader.]
my service Loader:-
class LagomPersistentEntityLoader extends LagomApplicationLoader {
override def load(context: LagomApplicationContext): LagomApplication =
new LagomPersistentEntityApplication(context) with AkkaDiscoveryComponents
override def loadDevMode(context: LagomApplicationContext): LagomApplication =
new LagomPersistentEntityApplication(context) with LagomDevModeComponents
override def describeService: Option[Descriptor] = Some(readDescriptor[LagomTestingEntity])
}
trait UserComponents
extends LagomServerComponents
with SlickPersistenceComponents
with HikariCPComponents
with AhcWSComponents {
override lazy val jsonSerializerRegistry: JsonSerializerRegistry = UserSerializerRegistry
lazy val userRepo: UserRepository = wire[UserRepository]
readSide.register(wire[UserEventsProcessor])
clusterSharding.init(
Entity(UserState.typeKey){ entityContext =>
UserBehaviour(entityContext)
}
)
}
abstract class LagomPersistentEntityApplication(context: LagomApplicationContext)
extends LagomApplication(context)
with UserComponents {
implicit lazy val actorSystemImpl: ActorSystem = actorSystem
implicit lazy val ec: ExecutionContext = executionContext
override lazy val lagomServer: LagomServer = serverFor[LagomTestingEntity](wire[LagomTestingEntityImpl])
lazy val bodyParserDefault: Default = wire[Default]
}
application.conf:-
play.application.loader = org.organization.service.LagomTestingEntityImpl
lagom-persistent-entity.cassandra.keyspace = lagom-persistent-entity
cassandra-journal.keyspace = ${lagom-persistent-entity.cassandra.keyspace}
cassandra-snapshot-store.keyspace = ${lagom-persistent-entity.cassandra.keyspace}
lagom.persistent.read-side.cassandra.keyspace = ${lagom-persistent-entity.cassandra.keyspace}
This service has both read side and write side support and If anyone need more info in code then please ask because I really wants to understand where the application is failing.
You should set play.application.loader to the name of your loader class, rather than your persistent entity class:
play.application.loader = org.organization.service.LagomPersistentEntityLoader
I'm assuming that LagomPersistentEntityLoader is in the same package as LagomTestingEntityImpl. If not, then adjust the fully-qualified class name as needed.
I am using Play 2.6 and Slick 3.2 with MySql database. But the problem is that I am not able to write test cases using h2 database. The problem is that, by using guice, I am not able to inject DatabaseConfigProvider in my repo. Following is my repo class implementation:
class CompanyRepoImpl #Inject() (dbConfigProvider: DatabaseConfigProvider)
(implicit ec: ExecutionContext) extends CompanyRepo {
val logger: Logger = LoggerFactory.getLogger(this.getClass())
private val dbConfig = dbConfigProvider.get[JdbcProfile]
import dbConfig._
import profile.api._
private val company = TableQuery[Companies]
------
}
My test case class:
class CompanyRepoSpec extends AsyncWordSpec with Matchers with BeforeAndAfterAll {
private lazy val injector: Injector = new GuiceApplicationBuilder()
.overrides(bind(classOf[CompanyRepo]).to(classOf[CompanyRepoImpl]))
.in(Mode.Test)
.injector()
private lazy val repo: CompanyRepo = injector.instanceOf[CompanyRepo]
private lazy val dbApi = injector.instanceOf[DBApi]
override protected def beforeAll(): Unit = {
Evolutions.applyEvolutions(database = dbApi.database("test"))
}
override protected def afterAll(): Unit = {
Evolutions.cleanupEvolutions(database = dbApi.database("test"))
}
-----------------------
}
Testing application.test.conf
play.evolutions.db.test.enabled=true
play.evolutions.autoApply=true
slick.dbs.test.profile="slick.jdbc.H2Profile$"
slick.dbs.test.db.driver="org.h2.Driver"
slick.dbs.test.db.url="jdbc:h2:mem:test;MODE=MySQL"
May be, my configuration are not valid for testing, so, how can we write test cases using scala, slick and play.
I have the following #Singleton in my Play for Scala application that loads on startup:
#Singleton
class Scheduler #Inject()(#Named("mainEtl") mainEtl: ActorRef, system: ActorSystem) {
// some code
}
This is the module where Scheduler is declared. The module is enabled in application.conf:
class Module extends AbstractModule {
def configure() = {
bind(classOf[Scheduler]).asEagerSingleton
}
}
And the related module definition to configure the #Named injected object, also declared in application.conf:
class AkkaBindings extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[MainEtl]("mainEtl")
}
}
When I run any ScalaTest test, apparently the singleton starts running because I get an error saying that it doesn't find MainEtl (the object injected in the Scheduler class). The point is that I don't need to run the singleton for my tests, so I need to disable it.
This is how I invoke the Play application in my tests:
class ManageBanksTest extends PlaySpec with OneAppPerSuite with MockitoSugar {
implicit override lazy val app = new GuiceApplicationBuilder().build
// more test code
}
This is how I tried to disable it, but it doesn't work as I get the same error:
implicit override lazy val app = new GuiceApplicationBuilder()
.disable[Scheduler]
.build
Alternatively, I could mock Scheduler, but I would have to mock also the #Named injected object and I couldn't find information on how to achieve that.
Any ideas?
This is the solution: to disable the Module class not to declare Scheduler as singleton:
implicit override lazy val app = new GuiceApplicationBuilder()
.disable[Module]
.build
import com.google.inject.AbstractModule
import com.google.inject.name.Names
import org.specs2.mock.Mockito
import play.api.inject.guice.{GuiceApplicationBuilder, GuiceableModule}
val modules = Option(new AbstractModule {
override def configure() = {
val mockMainETL = mock[MainEtl]
bind(classOf[ActorRef])
.annotatedWith(Names.named("mainEtl"))
.toInstance(mockMainETL)
val mock1 = mock[ManageBanksDAO]
mock1.readMany answers { _ => Future{seqMany}}
val mockManageBanks = mock[ManageBanks]
bind(classOf[ManageBanks]).toInstance(new ManageBanks(mock1))
}
})
lazy val app = new GuiceApplicationBuilder()
.overrides(modules.map(GuiceableModule.guiceable).toSeq: _*)
.build
Try configuring your mock inside modules and add those modules while initializing your application. This will inject custom mocks.
Also instead of this :
val controller = new ManageBanks(mock1)
Try this:
val controller = app.injector.instanceOf(classOf[ManageBanks])
I'm using scalatest_2.11 version 2.2.1. I'm trying to write test that run on a SparkContext. How can I initiate a SparkContext when the tests begin, take this Sc thorugh all the tests and then stop it when they are all done?
(I know that it suppose to stop by itself, but I'sd still like to do it myself)
You can use BeforeAndAfterAll from ScalaTest. Define a base trait that starts and stops the SparkContext and use it with your other tests.
trait SparkTest extends BeforeAndAfterAll {
self: Suite =>
#transient var sc: SparkContext = _
override def beforeAll {
val conf = new SparkConf().
setMaster("local[*]").
setAppName("test")
sc = new SparkContext(conf)
super.beforeAll()
}
override def afterAll: Unit = {
try {
sc.stop()
} finally {
super.afterAll
}
}
}
// Mix-in the trait with your tests like below.
class MyTest extends FunSuite with SparkTest {
test("my test") {
// you can access "sc" here.
}
}
I'm using play 2.3.8 and have some configuration in my GlobalSettings that change based on the mode of the application. So I have something like this:
object Global extends GlobalSettings {
override def onLoadConfig(config: Configuration, path: java.io.File, classloader: ClassLoader, mode: Mode.Mode) = {
println(mode)
val customConfig = //Based on mode.*
config ++ configuration ++ Configuration(ConfigFactory.parseMap(customConfig))
}
}
And then am trying to write tests to ensure that this behavior works:
class MyTest extends PlaySpec {
val testApp = FakeApplication(
additionalConfiguration = Map(
//SomeSettings And Stuff
"logger.application" -> "WARN",
"logger.root" -> "WARN"
)
)
val devApp = new FakeApplication(
additionalConfiguration = Map(
//SomeSettings And Stuff
"logger.application" -> "WARN",
"logger.root" -> "WARN"
)
) {
override val mode = Mode.Dev
}
val prodApp = new FakeApplication(
additionalConfiguration = Map(
//SomeSettings And Stuff
"logger.application" -> "WARN",
"logger.root" -> "WARN"
)
) {
override val mode = Mode.Prod
}
"ThisNonWorkingTestOfMine" must {
"when running application in test mode have config.thing = false" in running(testApp) {
assertResult(Mode.Test)(testApp.mode)
assertResult(false)(testApp.configuration.getBoolean("config.thing").get)
}
"when running application in dev mode have config.thing = false" in running(devApp) {
assertResult(Mode.Dev)(devApp.mode)
assertResult(false)(devApp.configuration.getBoolean("config.thing").get)
}
"when running application in prod mode have config.thing = true" in running(prodApp) {
assertResult(Mode.Prod)(prodApp.mode)
assertResult(true)(prodApp.configuration.getBoolean("config.thing").get)
}
}
}
And when I run these tests I see something a bit odd from my handy println:
Test
null
null
[info] MyTest:
[info] ThisNonWorkingTestOfMine
[info] play - Starting application default Akka system.
[info] play - Shutdown application default Akka system.
[info] - must when running application in test mode have config.thing = false
[info] play - Application started (Dev)
[info] - must when running application in dev mode have config.thing = false
[info] play - Application started (Prod)
[info] - must when running application in prod mode have config.thing = true *** FAILED ***
[info] Expected true, but got false (MyTest.scala:64)
[info] ScalaTest
How do I properly set the mode of the FakeApplication in Play 2.3? The way I have it now is based on a page from Mastering Play but clearly that isn't the way to go when using onLoadConfig it seems
Edit:
I'm also experimenting with OneAppPerTest and creating the FakeApplication in the newAppForTest method but it's still behaving oddly, with null's like the method above. This is really strange because if I set a random property like "foo" -> "bar" in the additionalConfiguration map when making my FakeApplication and then try to read it from config.getString in my Global object, it get's logged as None even though if I do app.configuration.getString in the test itself it shows bar. It feels like there is some type of disconnect here. And I don't get null for the mode if I use the FakeApplication.apply method rather than new FakeApplication
So I think this has something to do with the way that FakeApplication sets the mode to Mode.Test via override because if I copy the FakeApplication class and remove that line and create my own version of the class that let's me set the mode I have no issues. In other words, in my tests package I declare the following class:
package play.api.test
import play.api.mvc._
import play.api.libs.json.JsValue
import scala.concurrent.Future
import xml.NodeSeq
import play.core.Router
import scala.runtime.AbstractPartialFunction
import play.api.libs.Files.TemporaryFile
import play.api.{ Application, WithDefaultConfiguration, WithDefaultGlobal, WithDefaultPlugins }
case class FakeModeApplication(
override val path: java.io.File = new java.io.File("."),
override val classloader: ClassLoader = classOf[FakeModeApplication].getClassLoader,
val additionalPlugins: Seq[String] = Nil,
val withoutPlugins: Seq[String] = Nil,
val additionalConfiguration: Map[String, _ <: Any] = Map.empty,
val withGlobal: Option[play.api.GlobalSettings] = None,
val withRoutes: PartialFunction[(String, String), Handler] = PartialFunction.empty,
val mode: play.api.Mode.Value
) extends {
override val sources = None
} with Application with WithDefaultConfiguration with WithDefaultGlobal with WithDefaultPlugins {
override def pluginClasses = {
additionalPlugins ++ super.pluginClasses.diff(withoutPlugins)
}
override def configuration = {
super.configuration ++ play.api.Configuration.from(additionalConfiguration)
}
override lazy val global = withGlobal.getOrElse(super.global)
override lazy val routes: Option[Router.Routes] = {
val parentRoutes = loadRoutes
Some(new Router.Routes() {
def documentation = parentRoutes.map(_.documentation).getOrElse(Nil)
// Use withRoutes first, then delegate to the parentRoutes if no route is defined
val routes = new AbstractPartialFunction[RequestHeader, Handler] {
override def applyOrElse[A <: RequestHeader, B >: Handler](rh: A, default: A => B) =
withRoutes.applyOrElse((rh.method, rh.path), (_: (String, String)) => default(rh))
def isDefinedAt(rh: RequestHeader) = withRoutes.isDefinedAt((rh.method, rh.path))
} orElse new AbstractPartialFunction[RequestHeader, Handler] {
override def applyOrElse[A <: RequestHeader, B >: Handler](rh: A, default: A => B) =
parentRoutes.map(_.routes.applyOrElse(rh, default)).getOrElse(default(rh))
def isDefinedAt(x: RequestHeader) = parentRoutes.map(_.routes.isDefinedAt(x)).getOrElse(false)
}
def setPrefix(prefix: String) {
parentRoutes.foreach(_.setPrefix(prefix))
}
def prefix = parentRoutes.map(_.prefix).getOrElse("")
})
}
}
And then in my test I can use it like so:
val devApp = new FakeModeApplication(
additionalConfiguration = Map(
//SomeSettings And Stuff
"logger.application" -> "WARN",
"logger.root" -> "WARN"
), mode = Mode.Dev
)
And then the mode value comes through as what I set it to and not as null.
I'm posting this as an answer because it does solve the issue I'm facing, but I don't have an understanding of why the new keyword when making a FakeApplication like so: new FakeApplication() { override mode ... } causes the mode to come through as null in the onLoadConfig method on GlobalSettings. This feels like a hack rather than a solution and I'd appreciate if anyone with enough knowledge around this could post a solution that involves not copying the full FakeApplication class and changing one line.