Mock functional argument of the function - scala

I'm developing web app using Scala, Play Framework. And I need to test controller with the custom action. Please, take a look on the code:
package controllers.helpers
import play.api.mvc.{ActionBuilder, Request, Result}
import scala.concurrent.Future
class CustomAction extends ActionBuilder[Request] {
override def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]): Future[Result] = {
// do some stuff
block(request)
}
}
package controllers
import javax.inject.Singleton
import play.api.mvc.Controller
#Singleton
class SomeController #Inject() (customAction: CustomAction
) extends Controller {
def foo() = customAction(parse.json) { implicit request =>
// do some stuff and create result
}
}
And below you can find a code of the test class. I use Specs2 and I got org.mockito.exceptions.misusing.InvalidUseOfMatchersException on the line with customeActionMock.invokeBlock(any[Request[_]], any[(Request[_]) => Future[Result]]) returns mock[Future[Result]]
package controllers
import controllers.helpers.CustomAction
import org.specs2.mock.Mockito
import play.api.mvc.{Request, Result}
import play.api.test.{FakeHeaders, FakeRequest, PlaySpecification}
import scala.concurrent.Future
class SomeControllerSpec extends PlaySpecification with Mockito {
private val customeActionMock = mock[CustomAction]
customeActionMock.invokeBlock(any[Request[_]], any[(Request[_]) => Future[Result]]) returns mock[Future[Result]] //this doesn't work, exception is thrown there
"SomController" should {
"respond Ok on valid request" in {
val result = new UserController(customeActionMock).foo()(FakeRequest())
status(result) shouldEqual OK
}
}
}
I understand that I mock block parameter of the CustomAction incorrectly. Can someone help me to do it properly?

My project uses Play 2.5.x. I use scalatest. This is how I test controllers.
import org.scalatestplus.play.OneAppPerSuite
import org.scalatest._
import org.scalatest.time.{Millis, Seconds, Span}
import org.scalatest.concurrent.ScalaFutures
import scala.concurrent.Future
class SomeControllerSpec extends FlatSpec with Matchers with ScalaFutures with OneAppPerSuite {
private val customeActionMock = new CustomAction // create an instance of a class
implicit val defaultPatience = PatienceConfig(timeout = Span(5,Seconds), interval = Span(500, Millis))
it should "respond Ok on valid request" in {
val resultF : Future[Result] = new UserController(customeActionMock).foo()(FakeRequest())
whenReady(resultF) { resultR =>
resultR.header.status shouldBe 200
}
}
}

Don't mock the CustomAction :
class SomeControllerSpec extends PlaySpecification with Mockito {
private val customeActionMock = new CustomAction
"SomController" should {
"respond Ok on valid request" in {
val result = new UserController(customeActionMock).foo()(FakeRequest())
status(result) shouldEqual OK
}
}
}

Related

Stuck in infinite loop with simple akka-http route test with actors

I have a simple example where I have a route that invokes an actor, however it seems to get stuck in an infinite loop and the http response never comes. I am using akka-actor version 2.6.15 and akka-http version 10.2.4. Here is the sample code, any help is appreciated.
package test
import akka.actor.{Actor, ActorRef, Props}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{Route, _}
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
import akka.pattern.ask
import akka.util.Timeout
import org.scalatest.Matchers
import org.scalatest.wordspec.AnyWordSpec
import scala.concurrent.duration.DurationInt
case class TestMessage()
class TestActor extends Actor {
def receive: Receive = {
case _ => "response"
}
}
class AkkaHttpTest extends AnyWordSpec with Matchers with ScalatestRouteTest {
val testActor: ActorRef = system.actorOf(Props(new TestActor()), name = "TestActor")
implicit val timeout: Timeout = 15.seconds
implicit val defaultTimeout = RouteTestTimeout(15.seconds)
val route: Route = {
get {
pathSingleSlash {
complete((testActor ? TestMessage()).mapTo[String])
}
}
}
"Test" should {
"Return text" in {
Get() ~> route ~> check {
println(responseAs[String])
}
}
}
}
To reply to a message in Akka, you have to explicitly send the reply.
In your example:
def receive: Receive = {
case _ =>
sender ! "response"
}

could not find implicit value for parameter env: com.mohiva.play.silhouette.api.Environment[utils.auth.DefaultEnv]

I'm using a Silhouette v4.0 library with play framework 2.5.
And have been trying to write test code using play specs2.
But, I get the following error with my test class as below.
Error Message
[error] could not find implicit value for parameter env: com.mohiva.play.silhouette.api.Environment[utils.auth.DefaultEnv]
.withAuthenticator[DefaultEnv](identity.loginInfo)
^
Here's the test class
package controllers
import com.google.inject.AbstractModule
import org.joda.time.DateTime
import org.specs2.specification.Scope
import org.specs2.matcher._
import org.specs2.mock._
import play.api.test._
import play.api.libs.json._
import play.api.libs.json.Json
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.mailer.{ MailerClient, Email }
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.inject.bind
import com.mohiva.play.silhouette.test._
import com.mohiva.play.silhouette.api._
import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository
import com.mohiva.play.silhouette.api.util._
import com.mohiva.play.silhouette.impl.providers._
import net.codingwell.scalaguice.ScalaModule
import utils.auth.DefaultEnv
class TestControllerSpec extends PlaySpecification with Mockito {
"case" in new Context {
new WithApplication(application) {
val request = FakeRequest(POST, "/api/test")
.withAuthenticator[DefaultEnv](identity.loginInfo) // <-
val result = route(app, request).get
status(result) must be equalTo OK
}
}
trait Context extends Scope {
val identity = User(
loginInfo = LoginInfo(..)
..
)
implicit val env = FakeEnvironment[DefaultEnv](Seq(identity.loginInfo -> identity))
class FakeModule extends AbstractModule with ScalaModule {
def configure() = {
bind[Environment[DefaultEnv]].toInstance(env)
}
}
lazy val application = new GuiceApplicationBuilder()
.overrides(new FakeModule)
.build
}
}
There are some other test classes similar to this class are properly able to compile and execute.
It's kind of implicit problem with scope..
Therefore, I tried to import all the same as another test class which's able to compile properly. But, still unable to compile.
Missing some import?
As the compiler states, you're missing an implicit value. Use the following, which is modeled after one of Silhouette's specs:
class TestControllerSpec extends PlaySpecification with Mockito {
"the POST request" should {
"return an OK response" in new Context {
new WithApplication(application) {
val identity = User(LoginInfo(...))
implicit val env = FakeEnvironment[DefaultEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest(POST, "/api/test")
.withAuthenticator(identity.loginInfo)
val result = route(app, request).get
status(result) must be equalTo OK
}
}
}
trait Context extends Scope {
...
}
}

scala play framework how to unit test async controllers

Using Scala play version 2.5 and trying to follow the guidelines for unit testing controllers as per documentation at: https://www.playframework.com/documentation/2.5.x/ScalaTestingWithScalaTest
there's no example for unit testing an async controller.
I'm trying to create a unit test for my controller which has an async action method, I ended up mocking some objects
class ProductController #Inject()(
action: ProductAction,
handler: ProductResourceHandler)(implicit ec: ExecutionContext)
extends Controller {
/**
* Fetch a list of products
*/
def index: Action[AnyContent] = {
action.async { implicit request =>
handler.find.map { list =>
Ok(Json.toJson(list))
}
}
}
// ...
}
My unit test:
import scala.concurrent.Future
import org.scalatestplus.play._
import play.api.mvc._
import play.api.test._
import play.api.test.Helpers._
import org.scalatest.mockito.MockitoSugar
import product.ProductAction
import product.ProductController
import product.services.maps.GeolocationService
import product.ProductResourceHandler
import play.api.libs.concurrent.Execution.Implicits._
import scala.io.Source
import play.api.libs.json.Json
import product.model.OfferList
import product.model.OfferDetail
import org.mockito.Mockito._
class ProductControllerSpec extends PlaySpec with Results with MockitoSugar {
private val productList = Json.parse(Source.fromFile("conf/app/sample_products.json").getLines.mkString).as[ProductList]
"Example Page#index" should {
"should be valid" in {
val action = new ProductAction()
val handler = mock[ProductResourceHandler]
when(handler.find) thenReturn Future.successful(productList)
val controller = new ProductController(action, handler)
val result: Future[Result] = controller.index().apply(FakeRequest())
val bodyText: String = contentAsString(result)
bodyText != null mustBe true
}
}
}
up till now it's working but I'm wondering if this follows the best practices or guidelines for this type of test. Is this the right way to unit test an async controller in Scala play framework?
Some of my suggestions are
use contentAsJson instead of contentAsString and inspect the returned json.
use route to directly invoke the controller and test response (for eg route(app, FakeRequest..)
use status method to check if the returned status is HTTP OK (status code 200)
val Some(result) = route(app, FakeRequest(GET,
controllers.routes. ProductController.index.path()))
status(result) must be (OK)
val json = contentAsJson(result)
// inspect json fields like if you have to check if the json
// has string field called id you can do (json \ "id").as[String] must be ("<id value>")
According to Play documentation:
Play actions are asynchronous by default.
This means even if you are not using Action.async { Future { myAnonymousFunction } } but just Action { myAnonymousFunction }, internally the result of myAnonymousFunction will be enclosed in a Future.
For instance, say you have
class HelloWorld extends Controller {
def index = Action { request => Ok("") }
}
then
(new HelloWorld).index().apply(FakeRequest())
still has type
Future[Result]
This leads me to believe that your unit test is indeed appropriate way of testing controllers, that is, Play's documentation is implicitly covering also the case of Action.async.

How do I verify mockito calls in asynchronous methods

I'm writing a highly asynchronous bit of code and I'm having problems testing it using Specs2 and Mockito. The problem is that the matchers are executed before the asynchronous code executes. I see that specs2 has await and eventually helpers - they look promising but I'm not sure how to use them.
Below is a stripped down example that illustrates the problem
SUT
package example.jt
import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.util.{Try, Success, Failure}
trait Service {
def foo
def bar
def baz
}
class AnotherTest(svc: Service) {
def method(fail: Boolean) {
svc.baz
future {
Thread.sleep(3000)
pvt(fail) onComplete {
case Success(_) => svc.foo
case Failure(ex) => svc.bar
}
}
}
private def pvt(fail: Boolean):Future[Unit] = {
val p = Promise[Unit]
future {
Thread.sleep(2000)
if (fail) p failure (new RuntimeException("Failure"))
else p success ()
}
return p.future
}
}
Specs2 Test
package example.jt.test
import example.jt._
import org.specs2.specification._
import org.specs2.mutable._
import org.specs2.specification._
import org.specs2.mutable._
import org.specs2.mock._
class TestPromise extends Specification with Mockito {
"mocks in promises" should {
"Verify foo" in {
val svc = mock[Service]
val sut = new AnotherTest(svc)
sut.method(false)
there was one(svc).baz
there was one(svc).foo
there was no(svc).bar
}
"Verify bar" in {
val svc = mock[Service]
val sut = new AnotherTest(svc)
sut.method(true)
there was one(svc).baz
there was one(svc).bar
there was no(svc).foo
}
}
}
You simply need to wait on your future calls. Either by using Await directly:
import scala.concurrent._
import scala.concurrent.duration._
Await(sut.method(false), 10 seconds)
or by using .await on a matcher (look for await in the matchers guide):
sut.method(false) must not(throwAn[Exception]).await

Can Guice inject Scala objects

In Scala, can I use Guice to inject Scala objects?
For example, can I inject into s in the following object?
object GuiceSpec {
#Inject
val s: String = null
def get() = s
}
Some research on Google revealed that you can accomplish this as follows (the code that follows is a ScalaTest unit test):
import org.junit.runner.RunWith
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import org.scalatest.junit.JUnitRunner
import com.google.inject.Inject
import com.google.inject.Module
import com.google.inject.Binder
import com.google.inject.Guice
import uk.me.lings.scalaguice.ScalaModule
#RunWith(classOf[JUnitRunner])
class GuiceSpec extends WordSpec with MustMatchers {
"Guice" must {
"inject into Scala objects" in {
val injector = Guice.createInjector(new ScalaModule() {
def configure() {
bind[String].toInstance("foo")
bind[GuiceSpec.type].toInstance(GuiceSpec)
}
})
injector.getInstance(classOf[String]) must equal("foo")
GuiceSpec.get must equal("foo")
}
}
}
object GuiceSpec {
#Inject
var s: String = null
def get() = s
}
This assumes you are using scala-guice and ScalaTest.
The above answer is correct, but if you don't want to use ScalaGuice Extensions, you can do the following:
val injector = Guice.createInjector(new ScalaModule() {
def configure() {
bind[String].toInstance("foo")
}
#Provides
def guiceSpecProvider: GuiceSpec.type = GuiceSpec
})