Play Framework 2.5 Test ApplicationLifecycle Guice Specs2 setup - scala

I am trying to run some functional tests with play2-reactivemongo. I will try to be as concrete as possible, but if something is missing please let me know.
My dependencies are here
libraryDependencies ++= Seq(
cache,
"org.reactivemongo" %% "play2-reactivemongo" % "0.12.0",
"com.mohiva" %% "play-silhouette" % "4.0.0",
"com.mohiva" %% "play-silhouette-testkit" % "4.0.0" % "test",
specs2 % Test
)
In MongoUserDao.scala
import play.modules.reactivemongo._
import play.modules.reactivemongo.json._
import reactivemongo.play.json.collection.JSONCollection
class MongoUserDao #Inject() (val reactiveMongoApi: ReactiveMongoApi) extends UserDao {
val usersFuture = reactiveMongoApi.database.map(_.collection[JSONCollection]("users"))
...
}
In DaoSpecResources.scala
trait DaoSpecResources {
val timeout = DurationInt(10).seconds
val fakeApp = new GuiceApplicationBuilder()
.in(Mode.Test)
.configure(
"play.modules.enabled" -> List("play.modules.reactivemongo.ReactiveMongoModule"),
"mongodb.uri" -> "mongodb://localhost:27017/test"
)
.build
val reactiveMongoApi = fakeApp.injector.instanceOf[ReactiveMongoApi]
...
}
When I try to run the test I get this error
[error] cannot create an instance for class daos.UserDaoSpec
[error] caused by com.google.inject.CreationException: Unable to create injector, see the following errors:
[error]
[error] 1) No implementation for play.api.inject.ApplicationLifecycle was bound.
[error] while locating play.api.inject.ApplicationLifecycle
[error] for parameter 1 at services.ApplicationTimer.<init>(ApplicationTimer.scala:24)
[error] at Module.configure(Module.scala:23) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
[error]
[error] 1 error

This is caused by app/services/ApplicationTimer.scala, which depends on ApplicationLifecycle, but you haven't bound any implementation to ApplicationLifecycle. ApplicationTimer is a demo included in every new Play project. You should probably remove it completely if you don't need it, otherwise at least disable it if running in test mode. See disabling modules and overriding modules.
Solution
However, since DefaultReactiveMongoApi also depends on ApplicationLifecycle, you'll need to provide a binding to an implementation of ApplicationLifecycle. The easiest way:
import play.api.inject.{ ApplicationLifecycle, DefaultApplicationLifecycle }
import play.api.inject.bind
trait DaoSpecResources {
val timeout = DurationInt(10).seconds
val fakeApp = new GuiceApplicationBuilder()
.in(Mode.Test)
.configure(
"play.modules.enabled" -> List("play.modules.reactivemongo.ReactiveMongoModule"),
"mongodb.uri" -> "mongodb://localhost:27017/test"
)
.bindings(bind[ApplicationLifecycle].to[DefaultApplicationLifecycle])
.build
val reactiveMongoApi = fakeApp.injector.instanceOf[ReactiveMongoApi]
val lifecycle = fakeApp.injector.instanceOf[DefaultApplicationLifecycle]
def stopApp = lifecycle.stop()
}
(added 5 lines: imports, bindings, lifecycle and stopApp)
Then, in your test spec, add step(stopApp) at the end, like so:
class FooSpec extends PlaySpecification with DaoSpecResources {
// Your examples...
step(stopApp)
}
Alternative solution
Personally, I'd create a specialized trait extending Specification or PlaySpecification which would set up and tear down everything automatically, like in this example from specs2 documentation.
trait PlayWithMongoSpecification extends PlaySpecification {
val timeout = DurationInt(10).seconds
val fakeApp = new GuiceApplicationBuilder()
.in(Mode.Test)
.configure(
"play.modules.enabled" -> List("play.modules.reactivemongo.ReactiveMongoModule"),
"mongodb.uri" -> "mongodb://localhost:27017/test"
)
.bindings(bind[ApplicationLifecycle].to[DefaultApplicationLifecycle])
.build
val reactiveMongoApi = fakeApp.injector.instanceOf[ReactiveMongoApi]
val lifecycle = fakeApp.injector.instanceOf[DefaultApplicationLifecycle]
def stopApp = lifecycle.stop()
override def map(fs: =>Fragments) = fs ^ step(stopApp)
}
class FooSpec extends PlayWithMongoSpecification {
// Your examples...
}
You may consider making reactiveMongoApi a lazy val.

Related

Unit test LazyLogging using Mockito

I am having a class which extends LazyLogging trait
class TaskProcessor()
extends Processor
with LazyLogging {
def a1() = {
logger.info("Test logging")
}
}
Now, I want to test whether my logging works. So I followed this example Unit test logger messages using specs2 + scalalogging and wrote my test as follows
"TaskProcessor" should "test logging" in {
val mockLogger = mock[Logger]
val testable = new TaskProcessor {
override val logger: Logger = mockLogger
}
verify(mockLogger).info("Test logging")
}
I get the following error
Error:(32, 20) overriding lazy value logger in trait LazyLogging of type com.typesafe.scalalogging.Logger;
value logger must be declared lazy to override a concrete lazy value
override val logger: Logger = mockLogger
To resolve this, I modify statement
override val logger: Logger = mockLogger
to
override lazy val logger: Logger = mockLogger
I get the following error
Cannot mock/spy class com.typesafe.scalalogging.Logger
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class com.typesafe.scalalogging.Logger
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
at org.scalatest.mockito.MockitoSugar.mock(MockitoSugar.scala:73)
at org.scalatest.mockito.MockitoSugar.mock$(MockitoSugar.scala:72)
My dependecies are as follows
"org.scalatest" %% "scalatest" % "3.0.5" % "test",
"org.mockito" % "mockito-all" % "1.10.19" % Test,
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
Can anyone please guide me as to how I can mock the logger and do the testing.
The problem is that com.typesafe.scalalogging.Logger class cannot be mocked because it's final, but we still can mock underlying org.slf4j.Logger.
import org.scalatest.mockito.MockitoSugar
import org.slf4j.{Logger => UnderlyingLogger}
import com.typesafe.scalalogging.Logger
import org.scalatest.{Matchers, WordSpec, FlatSpec}
import org.mockito.Mockito._
class TaskProcessorSpec extends FlatSpec with Matchers with MockitoSugar {
"TaskProcessor" should "test logging" in {
val mockLogger = mock[UnderlyingLogger]
when(mockLogger.isInfoEnabled).thenReturn(true)
val testable = new TaskProcessor {
override lazy val logger = Logger(mockLogger)
}
testable.a1()
verify(mockLogger).info("Test logging")
}
}

ambiguous implicit values: match expected type cats.derived.MkShow[A]: show cats:kittens

I'm trying to create Show Instance for my custom Config class.
The build.sbt file is -
name := "circe-demo"
version := "0.1"
scalaVersion := "2.11.12"
resolvers += Resolver.bintrayRepo("ovotech", "maven")
libraryDependencies += "io.circe" %% "circe-core" % "0.11.0"
libraryDependencies += "io.circe" %% "circe-parser" % "0.11.0"
libraryDependencies += "io.circe" %% "circe-generic" % "0.11.0"
libraryDependencies += "org.typelevel" %% "kittens" % "1.2.0"
libraryDependencies ++= Seq(
"is.cir" %% "ciris-cats",
"is.cir" %% "ciris-cats-effect",
"is.cir" %% "ciris-core",
"is.cir" %% "ciris-enumeratum",
"is.cir" %% "ciris-refined"
).map(_ % "0.12.1")
Complete code is -
import enumeratum.{Enum, EnumEntry}
sealed abstract class AppEnvironment extends EnumEntry
object AppEnvironment extends Enum[AppEnvironment] {
case object Local extends AppEnvironment
case object Testing extends AppEnvironment
case object Production extends AppEnvironment
override val values: Vector[AppEnvironment] =
findValues.toVector
}
import java.net.InetAddress
import scala.concurrent.duration.Duration
final case class ApiConfig(host: InetAddress, port: Int, apiKey: String, timeout: Duration)
import java.net.InetAddress
import cats.Show
import cats.derived.semi
import ciris.config.loader.AppEnvironment.{Local, Production, Testing}
import enumeratum.EnumEntry
import eu.timepit.refined.auto._
import eu.timepit.refined.types.string.NonEmptyString
import scala.concurrent.duration._
final case class Config(appName: NonEmptyString, environment: AppEnvironment, api: ApiConfig)
object Config {
implicit val showConfig: Show[Config] = {
implicit val showDuration: Show[Duration] =
Show.fromToString
implicit val showInetAddress: Show[InetAddress] =
Show.fromToString
implicit def showEnumEntry[E <: EnumEntry]: Show[E] =
Show.show(_.entryName)
// Show.show[Config](x => s"api = ${x.api} appName = ${x.appName} environment ${x.environment}")
semi.show
}
}
semi.show in the above code throws the below exception -
[error] /Users/rajkumar.natarajan/Documents/Coding/kafka_demo/circe-demo/src/main/scala/ciris/config/loader/Config.scala:32:5: ambiguous implicit values:
[error] both value emptyProductDerivedShow in trait MkShowDerivation of type => cats.derived.MkShow[shapeless.HNil]
[error] and method emptyCoproductDerivedShow in trait MkShowDerivation of type => cats.derived.MkShow[shapeless.CNil]
[error] match expected type cats.derived.MkShow[A]
[error] show
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error]
I'm new to functional programming using cats.
How can I resolve this exception.
Unfortunately error reporting when such complicated implicits and macros are involved is far from perfect. The message you see actually means that some required implicits for the real generator (MkShow.genericDerivedShowProduct in this case) have not been found and the search went back to some where basic stuff where there is an ambiguity. And the stuff that is missing is mostly very basic such as an implicit for Show[Int] or Show[String]. The simplest way to get them all is to import cats.implicits._ but that will also bring catsStdShowForDuration which is a Show[Duration]. But since it's implementation is really the same as your custom one, it is easier to remove your custom one. One more thing that is missing is Show[NonEmptyString] and it is easy to create one
implicit def showNonEmptyString: Show[NonEmptyString] = Show.show(nes => nes)
To sum up, when I define your showConfig as
implicit val showConfig: Show[Config] = {
import cats.implicits._
// is already defined in cats.implicits._
//implicit val showDuration: Show[Duration] = Show.fromToString
implicit val showInetAddress: Show[InetAddress] = Show.fromToString
implicit def showEnumEntry[E <: EnumEntry]: Show[E] = Show.show(_.entryName)
implicit def showNonEmptyString: Show[NonEmptyString] = Show.show(nes => nes)
// Show.show[Config](x => s"api = ${x.api} appName = ${x.appName} environment ${x.environment}")
semi.show
}
it compiles for me.
P.S. is there any good reason why you put your AppEnvironment under ciris.* package? I'd say that generally putting your custom code into packages of 3-rd party library is an easy way to mess things up.

Specs2: how to test a class with more than one injected dependency?

Play 2.4 app, using dependency injection for service classes.
I found that Specs2 chokes when a service class being tested has more than one injected dependency. It fails with "Can't find a constructor for class ..."
$ test-only services.ReportServiceSpec
[error] Can't find a constructor for class services.ReportService
[error] Error: Total 1, Failed 0, Errors 1, Passed 0
[error] Error during tests:
[error] services.ReportServiceSpec
[error] (test:testOnly) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 2 s, completed Dec 8, 2015 5:24:34 PM
Production code, stripped to bare minimum to reproduce this problem:
package services
import javax.inject.Inject
class ReportService #Inject()(userService: UserService, supportService: SupportService) {
// ...
}
class UserService {
// ...
}
class SupportService {
// ...
}
Test code:
package services
import javax.inject.Inject
import org.specs2.mutable.Specification
class ReportServiceSpec #Inject()(service: ReportService) extends Specification {
"ReportService" should {
"Work" in {
1 mustEqual 1
}
}
}
If I remove either UserService or SupportService dependency from ReportService, the test works. But obviously the dependencies are in the production code for a reason. Question is, how do I make this test work?
Edit: When trying to run the test inside IntelliJ IDEA, the same thing fails, but with different messages: "Test framework quit unexpectedly", "This looks like a specs2 exception..."; see full output with stacktrace. I opened a Specs2 issue as instructed in the output, though I have no idea if the problem is in Play or Specs2 or somewhere else.
My library dependencies below. (I tried specifying Specs2 version explicitly, but that didn't help. Looks like I need specs2 % Test as is, for Play's test classes like WithApplication to work.)
resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases"
libraryDependencies ++= Seq(
specs2 % Test,
jdbc,
evolutions,
filters,
"com.typesafe.play" %% "anorm" % "2.4.0",
"org.postgresql" % "postgresql" % "9.4-1205-jdbc42"
)
There is limited support for dependency injection in specs2, mostly for execution environments or command-line arguments.
There is nothing preventing you from just using a lazy val and your favourite injection framework:
class MySpec extends Specification with Inject {
lazy val reportService = inject[ReportService]
...
}
With Play and Guice, you could have a test helper such as this:
import play.api.inject.guice.GuiceApplicationBuilder
import scala.reflect.ClassTag
trait Inject {
lazy val injector = (new GuiceApplicationBuilder).injector()
def inject[T : ClassTag]: T = injector.instanceOf[T]
}
If you really need runtime dependency injection, then it's better to use Guice loading, I guess:
package services
import org.specs2.mutable.Specification
import scala.reflect.ClassTag
import com.google.inject.Guice
// Something you'd like to share between your tests
// or maybe not
object Inject {
lazy val injector = Guice.createInjector()
def apply[T <: AnyRef](implicit m: ClassTag[T]): T =
injector.getInstance(m.runtimeClass).asInstanceOf[T]
}
class ReportServiceSpec extends Specification {
lazy val reportService: ReportService = Inject[ReportService]
"ReportService" should {
"Work" in {
reportService.foo mustEqual 2
}
}
}
Alternatively you can implement Inject object as
import scala.reflect.ClassTag
import play.api.inject.guice.GuiceApplicationBuilder
object Inject {
lazy val injector = (new GuiceApplicationBuilder).injector()
def apply[T : ClassTag]: T = injector.instanceOf[T]
}
It depends whether you want to use Guice directly, or thru play wrappers.
Looks like you are out of luck ATM: The comment says
Try to create an instance of a given class by using whatever constructor is available and trying to instantiate the first parameter recursively if there is a parameter for that constructor.
val constructors = klass.getDeclaredConstructors.toList.filter(_.getParameterTypes.size <= 1).sortBy(_.getParameterTypes.size)
i.e. Specs2 doesn't provide own DI out-of-the box,
Or you can reimplement the functionality yourself, if Guice isn't working for you.
App code:
package services
import javax.inject.Inject
class ReportService #Inject()(userService: UserService, supportService: SupportService) {
val foo: Int = userService.foo + supportService.foo
}
class UserService {
val foo: Int = 1
}
class SupportService {
val foo: Int = 41
}
Test code
package services
import org.specs2.mutable.Specification
import scala.reflect.ClassTag
import java.lang.reflect.Constructor
class Trick {
val m: ClassTag[ReportService] = implicitly
val classLoader: ClassLoader = m.runtimeClass.getClassLoader
val trick: ReportService = Trick.createInstance[ReportService](m.runtimeClass, classLoader)
}
object Trick {
def createInstance[T <: AnyRef](klass: Class[_], loader: ClassLoader)(implicit m: ClassTag[T]): T = {
val constructors = klass.getDeclaredConstructors.toList.sortBy(_.getParameterTypes.size)
val constructor = constructors.head
createInstanceForConstructor(klass, constructor, loader)
}
private def createInstanceForConstructor[T <: AnyRef : ClassTag]
(c: Class[_], constructor: Constructor[_], loader: ClassLoader): T = {
constructor.setAccessible(true)
// This can be implemented generically, but I don't remember how to deal with variadic functions
// generically. IIRC even more reflection.
if (constructor.getParameterTypes.isEmpty)
constructor.newInstance().asInstanceOf[T]
else if (constructor.getParameterTypes.size == 1) {
// not implemented
null.asInstanceOf[T]
} else if (constructor.getParameterTypes.size == 2) {
val types = constructor.getParameterTypes.toSeq
val param1 = createInstance(types(0), loader)
val param2 = createInstance(types(1), loader)
constructor.newInstance(param1, param2).asInstanceOf[T]
} else {
// not implemented
null.asInstanceOf[T]
}
}
}
// NB: no need to #Inject here. The specs2 framework does it for us.
// It sees spec with parameter, and loads it for us.
class ReportServiceSpec (trick: Trick) extends Specification {
"ReportService" should {
"Work" in {
trick.trick.foo mustEqual 2
}
}
}
And that expectedly fails with
[info] ReportService should
[error] x Work
[error] '42' is not equal to '2' (FooSpec.scala:46)
If you don't need runtime dependency injection, then it's better to use cake pattern, and forget reflection all-together.
My colleague suggested a "low-tech" workaround. In the test, instantiate service classes with new:
class ReportServiceSpec extends Specification {
val service = new ReportService(new UserService, new SupportService)
// ...
}
This also works:
class ReportServiceSpec #Inject()(userService: UserService) extends Specification {
val service = new ReportService(userService, new SupportService)
// ...
}
Feel free to post more elegant solutions. I've yet to see a simple DI solution that works (with Guice, Play's default).
Does anyone else find it curious that Play's default test framework does not play well with Play's default DI mechanism?
Edit: In the end I went with an "Injector" test helper, almost the same as what Eric suggested:
Injector:
package testhelpers
import play.api.inject.guice.GuiceApplicationBuilder
import scala.reflect.ClassTag
/**
* Provides dependency injection for test classes.
*/
object Injector {
lazy val injector = (new GuiceApplicationBuilder).injector()
def inject[T: ClassTag]: T = injector.instanceOf[T]
}
Test:
class ReportServiceSpec extends Specification {
val service = Injector.inject[ReportService]
// ...
}

scala Play Framework 2.4: sending email

I trying to send an email from scala Play framework 2.4 while using play-mailer, I have followed the instruction from their sample page but with no success.
I have added the dependency to build.sbt:
libraryDependencies ++= Seq(
"com.typesafe.play" %% "play-mailer" % "3.0.1"
)
In application.conf the I have added the following:
play.mailer {
host=smtp.gmail.com
port=465
ssl=true
tls=true
user="testme#gmail.com"
password=abracadabra
}
And finally, the Mailing Class:
package controllers
import java.io.File
import javax.inject.Inject
import org.apache.commons.mail.EmailAttachment
import play.api.Configuration
import play.api.Play.current
import play.api.libs.mailer._
class Mail(mailer: MailerClient) {
def send = {
val cid = "1234"
val email = Email(
"Simple email",
"Mister FROM <from#email.com>",
Seq("Miss TO <to#email.com>"),
bodyText = Some("A text message"),
bodyHtml = Some("some data....")
)
mailer.send(email)
}
}
So far without compilation errors, however I don't understand how to initialize this class.. how should I get the "MailerClient" instance?
In the documentation it is written "Then in your router definition, use the trait MailerComponents", with the following code example:
import play.api._
import play.api.ApplicationLoader.Context
import router.Routes
import play.api.libs.mailer._
class MyApplicationLoader extends ApplicationLoader {
def load(context: Context) = {
new ApplicationComponents(context).application
}
}
class ApplicationComponents(context: Context) extends BuiltInComponentsFromContext(context) with MailerComponents {
lazy val myComponent = new MyComponent(mailerClient)
// create your controllers here ...
lazy val router = new Routes(...) // inject your controllers here
}
(I have added "play.application.loader=SimpleApplicationLoader" in application.conf)
but I get the following compilation errors:
D:\myApp\app\SimpleApplicationLoader.scala:12: not found: type MailerComponents
[error] class ApplicationComponents(context: Context) extends BuiltInComponentsFromContext(context) with MailerComponents {
[error] ^
[error] D:\myApp\app\SimpleApplicationLoader.scala:13: not found: value mailerClient
[error] lazy val applicationController = new controllers.Mail(mailerClient)
[error] ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
Any ideas?
You can go with the run-time dependency injection, as the other answer has suggested. But if you want to go with your current approach, read on...
The problem is, the MailerComponents trait doesn't exist in the 3.x branch. it does seem to exist in the master branch, but not in their next branch. I am not sure what they are doing there.
If you want to continue with the example, you'll need to do a bit of fiddling and figure out how to make it compile. For ex. with a bit of looking around, I came up with the following.
import play.api._
import play.api.ApplicationLoader.Context
import router.Routes
import play.api.libs.mailer._
class SimpleApplicationLoader extends ApplicationLoader {
def load(context: Context) = {
new ApplicationComponents(context).application
}
}
class ApplicationComponents(context: Context) extends BuiltInComponentsFromContext(context) {
val mailerClient = new CommonsMailer(configuration)
lazy val applicationController = new controllers.ApplicationScala(mailerClient)
lazy val assets = new controllers.Assets(httpErrorHandler)
lazy val router = new Routes(httpErrorHandler, applicationController)
}
Basically, instead of relying on the non-existent MailerComponent to create a mailerClient for me, I just did it myself.
If you have the following line in controllers.ApplicationScala
val id = mailer.configure(Configuration.from(Map("host" -> "typesafe.org", "port" -> 1234))).send(email)
Replace it with:
val id = mailer.send(email)
And it will compile. Meanwhile, I think I should raise an issue on github about this. And maybe you should.
I believe you could simply let play framework inject the mail client. Setting up the configuration and annotating your constructor with #Inject() should work.
Declaration of your Mail controller would look like this:
class Mail #Inject() (mailer: MailerClient) {

Basic Spray-Testkit usage to test a route does not work

I am trying to use spray route and want to test it with Spray-TestKit.
I am using :
- Scala 2.10.3
- Akka 2.3.3
- Spray 1.3.1
I create a trait extending HttpService, where I define a route :
trait MyService extends HttpService with CoreAccess {
import greentee.am.endpoint.tsmsp.tsmSPJsonSupport._
val myRoute = {
path("resources"/"ping") {
get {
complete(OK, "pong")
}
}
}
}
I deleted part of the route which was not relevant.
CoreAccess is a trait extending Actor, because I have methods in that trait access the ActorSystem. (I don't know who to retrieve ActorSelection from a trait without it extending an actor)
Then I create a test Specification
import MyService
import org.specs2.mutable.Specification
import spray.testkit.Specs2RouteTest
import spray.http.StatusCodes._
class RegistrationRouteSpecification extends Specification with Specs2RouteTest with MyService {
def actorRefFactory = system
"The EndPoint " should {
"return Pong to a Get request to the ping" in {
Get("/resources/ping") ~> myRoute ~> check {
status === OK
responseAs[String] === "pong"
}
}
}
}
When I try to execute the test, I get the following compilation error:
[info] Compiling 1 Scala source to /Users/IdeaProjects/endpoint/target/scala-2.10/test-classes...
[error] /Users/IdeaProjects/endpoint/src/test/scala/RegistrationRouteSpecification.scala:19: could not find implicit value for parameter ta: RegistrationRouteSpecification.this.TildeArrow[spray.routing.RequestContext,Unit]
[error] Get("/resources/ping") ~> myRoute ~> check {
[error] ^
[error] one error found
I answer my own question.
I corrected my Build.scala to use the following lines:
val scalaCheck = "org.scalacheck" %% "scalacheck" % Versions.scalaCheckVersion % "test"
val scalaTest = "org.scalatest" %% "scalatest" % "2.2.0" % "test"
Instead of using a simple '%' and supplying a dedicated version.