Let say, i have controller like the following:
class JenisKejahatanControl #Inject()(service: JenisKejahatanService, val messagesApi: MessagesApi) extends Controller with I18nSupport {
def add = Action.async { implicit request =>
lazy val incoming = JenisKejahatan.formJenisK.bindFromRequest()
incoming.fold( error => {
lazy val response = ErrorResponse(BAD_REQUEST, messagesApi("request.error"))
Future.successful(BadRequest(Json.toJson(response)))
}, { newJenisK =>
lazy val future = service.addJenisK(newJenisK)
future.flatMap {
case Some(jenis) => Future.successful(Created(Json.toJson(SuccessResponse(jenis))))
case None => Future.successful(BadRequest(Json.toJson(ErrorResponse(NOT_FOUND, messagesApi("add.jenis.kejahatan.fail")))))
}
})
}
}
and the i want to test my def add using specs2, how to do it?
Since your controller has injected components the bit I assume that one bit you're missing is how to obtain an instance of it in your spec with the various dependencies satisfied. For this you can use the GuiceApplicationBuilder to obtain a Play application instance, and then use its injector to get an instance of your controller without having to construct it manually (more dependency injection docs here and specifically about testing with Guice here.)
If you can construct your controller manually, as in the example, that's great and makes things simpler, but controllers tend to have non-trivial dependencies which you will most likely want to mock using the overrides method on the GuiceApplicationBuilder.
With an instance of your controller constructed, it's then really simple to "apply" a mock (fake) request to your action methods and determine that they give the status and body you expect. Here's an example:
import controllers.{SuccessResponse, JenisKejahatanControl}
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.inject.bind
import play.api.mvc.Result
import play.api.test.{FakeRequest, PlaySpecification}
import scala.concurrent.Future
class JenisKejahatanControlSpec extends PlaySpecification {
"JenisKejahatanControl#add" should {
"be valid" in {
// Build an instance of a Play application using the
// default environment and configuration, and use it
// to obtain an instance of your controller with the
// various components injected.
val jenisController = new GuiceApplicationBuilder()
.overrides( // Mock the data service
bind[JenisKejahatanService]
.toInstance(new MockJenisKejahatanService))
.build()
.injector
.instanceOf[JenisKejahatanControl]
// Create a "fake" request instance with the appropriate body data
val request = FakeRequest().withFormUrlEncodedBody("name" -> "test")
// Apply the request to your action to obtain a response
val eventualResult: Future[Result] = jenisController.add.apply(request)
// Check the status of the response
status(eventualResult) must_== CREATED
// Ensure the content of the response is as-expected
contentAsJson(eventualResult).validate[SuccessResponse].asOpt must beSome.which { r =>
r.jenis.name must_== "test"
}
}
}
}
Related
I have been going through documentations on ScalaTest but able to figure out what type of approach i should take for testing the app.
Code is divided amoung controller and service.
eg. Controller Code example
#Singleton
class Controller1 #Inject()(service1: ServiceClass1, authAction : AuthAction)
extends InjectedController {
//returns a list[]
def getSomeValue() = authAction {
val res = service1.getValue1()
val json = Json.toJson(res)
Ok(json)
}
}
Service Code Example -:
def getValue1() = {
implicit val graph = db.g
val infos = graph.V.hasLabel[someModel].toList()
infos.map(vertex => {
val someModel = vertex.toCC[someModel]
val item = info(someId =
someModel.someId.getOrElse("").toString,
category = SomeModel.category,
description = someModel.description)
item
})
}
I am very new to Testing and Scala both, I also understand the code but not able to understand where to begin.
This is just a sample code which is very similar.
It seems like what you're looking for is a way to mock service1.getValue1() in your Controller1.
Scalatest supports a couple different ways to do this: http://www.scalatest.org/user_guide/testing_with_mock_objects
In your case, to test def getSomeValue(); you'd need to define a mock and set the right expectations so that when called from the test, the mock returns the expected responses.
If you'd like to use scala mock, you'd need to add it as a dependency in your sbt build config. You can do that by adding this dependency to your tests:
"org.scalamock" %% "scalamock" % "4.4.0" % Test
And then, your test could be something like this:
import org.scalamock.scalatest.MockFactory
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class Controller1Spec extends AnyFlatSpec with Matchers with MockFactory {
"Controller1" should "respond with a valid response" in {
val mockService = mock[ServiceClass1]
(mockService.getValue1 _).when().returning("Some Response").once()
val mockAuthAction = mock[AuthAction] //assuming you've got an action called AuthAction
//you'd need to mock this one too, in order for it to work
(mockAuthAction.invokeBlock _) expects(_) onCall((r,b) => b(r))
new Controller1(mockService, mockAuthAction) shouldBe Ok("Some Response")
}
}
There's a number of posts on mocking for Scala which you should be able to find, like this one here: https://scalamock.org/user-guide/advanced_topics/
I am trying to use Play 2.5 dependency injection. I have following class which makes a call to REST api and parses the response
class Client #Inject()(ws:WSClient, baseUrl: string) {
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {....}
....
}
The caller of the code looks like below
var client = new Client(WS.client)
client.getResponse()
I am getting following warning.
object WS in package ws is deprecated: Inject WSClient into your
component
I understand that i need to inject WS.Client instead of passing it explicitly to the Client constructor. But how do i do that?
=== Update ===
I don't want to inject Client or WSClient from the Controller. My controller creates objects and classes at run time and i want those objects to create Client Object. When i explicitly pass WS.client object to the Client object i get the above stated warning.
=== Update 2 ===
I have a plugin architecture in my application. When a a controller starts an action. It does not know what set of plugins it is going to execute. Some plugins would not need a WSClient and some of them would. So i dont want to couple the injection of WSClient into my controller. Each plugin independently decides if it wants to call a remote service. When a plugin decides to call the remote service, it should be able to inject WSClient in what ever client it wants to invoke.
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1 (needs to call a remote api, create a client object, per say new Client(WS.Client)). This is where the injection should happen, not at the controller.
Ok. I will assume you have two classes. First we will have your Client class:
#Singleton // this is not necessary, I put it here so you know this is possible
class Client #Inject() (ws:WSClient, baseUrl: String) {
// Since this controller is not annotated with #Inject
// it WILL NOT be used when binding components
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {
// do something using ws object
}
}
Then you have another class that uses Client, per instance, a controller:
class MyController #Inject() (client: Client) extends Controller {
def someAction = Action {
// do something with client object
}
}
The main point here is that the controller did not need to create a Client instance. It was automatically injected by Guice.
Moreover, your client class needs a baseUrl and there is no place telling Play which value is needed there. If this is a configuration, than you can do something like this:
import play.api.Configuration
class Client #Inject() (ws:WSClient, configuration: Configuration) {
def getResponse() = {
val baseUrl = configuration.getString("key.to.baseUrl")
// do something using ws object and baseUrl
}
}
But, if you really want your Client object to receives a String, then we need to tell Play which String needs to be injected:
package com.acme.modules
import com.google.inject.AbstractModule
import com.google.inject.name.Names
class MyModule extends AbstractModule {
def configure() = {
bind(classOf[String])
.annotatedWith(Names.named("baseUrl")) // attention to the name here. It will be used below
.toInstance("http://api.example.com/")
}
}
And then enable this module by adding the following line to your application.conf:
play.modules.enabled += "com.acme.modules.MyModule"
After that, we will change Client to be specific about which String it is expecting:
import play.api.Configuration
// #Named needs to receive the same value defined at the module class.
class Client #Inject() (ws:WSClient, #Named("baseUrl") baseUrl: String) {
def getResponse() = {
val baseUrl = configuration.getString("key.to.baseUrl")
// do something using ws object and baseUrl
}
}
Update after question edit:
Give the structure you want/need:
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1
Your code can also follow that path with classes like this:
MyController -> PluginResolver -> Plugin
-> PluginRunner ->
And, then, you can have:
Controller:
class MyController #Inject() (
pluginResolver: PluginResolver,
pluginRunner: PluginRunner
) extends Controller {
def action = Action {
val plugins = pluginsResolver.resolve(/* give a criteria to select plugins */)
val someResultFromPluginsExecution = pluginsRunner.run(plugins)
// map result from plugins execution to a play play.api.mvc.Result
// return the play.api.mvc.Result
}
}
Plugin classes:
import play.api.inject.Injector
class PluginResolver #Inject()(injector: Injector) {
def resolve(/* some criteria to resolve plugins */): Seq[Plugin] = {
val pluginsClasses = ... // find the necessary plugins based on the criteria
pluginsClasses.map { pluginClass => injector.instanceOf(pluginClass) }
}
}
// ExecutionContext is not really necessary, but maybe you want/need
// another thread pool to execute plugins
class PluginRunner #Inject()(implicit executionContext: ExecutionContext) {
def run(plugins: Seq[Plugin]): Seq[PluginExecutionResult] = {
// run the plugins
// return the result
}
}
trait Plugin {
def execute(): PluginExecutionResult
}
The real magic here happens at the PluginResolver. It uses a play.api.inject.Injector to create plugins instances and then your plugins can use Dependency Injection. Per instance:
class PluginThatNeedsWSClient #Inject(wsClient: WSClient) extends Plugin {
def execute(): PluginExecutionResult = {
// Use wsClient to call a remote service
// return the execution result
}
}
Reference:
Scala: Dependency Injection
Scala: Play WS API
play.api.inject.Injector
I saw this awesome post last week: http://www.schibsted.pl/2016/04/dependency-injection-play-framework-scala/
I'm using Play! 2.4 with Deadbolt2 for authorization. However, since I introduced the authorization rules, I'm unable to write successful tests for my controllers. As an example:
class VisitController #Inject() (authorization: DeadboltActions) extends Controller {
def fetchDailyVisits(date: Date) = authorization.Restrict(List(Array(ADMIN_ROLE), Array(MANAGER_ROLE))) {
Action.async {
visitService.findDailyVisits(date).map(result =>
Ok(Json.toJson(result))
)
}
}
}
I'm using specs2 in the tests. My test looks like this atm:
class VisitControllerSpec extends PlaySpecification with Mockito with ScalaFutures {
val deadboltActions = mock[DeadboltActions]
"VisitControllerSpec#fetchDailyVisits" should {
val testDate = Date.from(LocalDate.of(2016, 2, 25)
.atStartOfDay(ZoneId.systemDefault()).toInstant)
"Return Status Ok with returned list" in {
val expected = List(completeVisitWithId, anotherCompleteVisitWithId)
visitService.findDailyVisits(testDate) returns Future { expected }
val request = FakeRequest(GET, "/visits?date=2016-02-25")
val result = new VisitController(deadboltActions)
.fetchDailyVisits(testDate)(request)
result.futureValue.header.status must beEqualTo(OK)
contentAsJson(result) must_== Json.toJson(expected)
}
}
}
How do I mock deadboltActions in a way I can specify the user will be allowed access?
Is there another way? Maybe by providing a different DeadboltHandler? It seems kind of obvious this would be the way to go, I just don't seem to be able to figure it out and there aren't a lot of Deadbolt2 examples out there (at least for scala).
Or, being more extreme, any other authorization framework out there that works well with scala play and allows to handle security as a cross-cutting concern without poluting the controllers? Deadbolt2 is too limited for this reason, but I honestly can't find a better authorization framework (unless I write my own).
There are a number of different ways you can do this.
If your DeadboltHandler has a DAO injected for accessing the subject, you can override the binding of the DAO to provide one containing test subjects.
abstract class AbstractControllerSpec extends PlaySpecification {
sequential
isolated
def testApp: Application = new GuiceApplicationBuilder().in(Mode.Test).bindings(bind[SubjectDao].to[TestSubjectDao]).build()
}
See the test app for an example of using this approach.
Alternatively, you can extend your DeadboltHandler implementation to override getSubject and provide a test subject from here. The binding is handled in the same way as above.
Finally, you can keep all your code as-is and populate the test database with subjects; the requests you send will be shaped by your authentication requirements (headers, something in a cookie, etc).
For unit testing, something similar applies. Given a SubjectDao that has some hard-coded subjects for test purposes, you can use WithApplication and an injector look-up to get what you need.
class TestSubjectDao extends SubjectDao {
val subjects: Map[String, Subject] = Map("greet" -> new SecuritySubject("greet",
List(SecurityRole("foo"),
SecurityRole("bar")),
List(SecurityPermission("killer.undead.zombie"))),
"lotte" -> new SecuritySubject("lotte",
List(SecurityRole("hurdy")),
List(SecurityPermission("killer.undead.vampire"))),
"steve" -> new SecuritySubject("steve",
List(SecurityRole("bar")),
List(SecurityPermission("curator.museum.insects"))),
"mani" -> new SecuritySubject("mani",
List(SecurityRole("bar"),
SecurityRole("hurdy")),
List(SecurityPermission("zombie.movie.enthusiast"))),
"trippel" -> new SecuritySubject("trippel",
List(SecurityRole("foo"),
SecurityRole("hurdy")),
List[SecurityPermission]()))
override def user(userName: String): Option[Subject] = subjects.get(userName)
}
With a controller that looks something like this:
class Subject #Inject()(deadbolt: DeadboltActions) extends Controller {
def subjectMustBePresent = deadbolt.SubjectPresent()() { authRequest =>
Future {
Ok("Content accessible")
}
}
}
We can then unit test it like this:
import be.objectify.deadbolt.scala.DeadboltActions
import be.objectify.deadbolt.scala.test.controllers.composed.Subject
import be.objectify.deadbolt.scala.test.dao.{SubjectDao, TestSubjectDao}
import play.api.Mode
import play.api.inject._
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.mvc.{Result, Results}
import play.api.test.{FakeRequest, PlaySpecification, WithApplication}
import scala.concurrent.Future
object SubjectPresentUnitSpec extends PlaySpecification with Results {
"Subject present " should {
"should result in a 401 when no subject is present" in new WithApplication(new GuiceApplicationBuilder().in(Mode.Test).bindings(bind[SubjectDao].to[TestSubjectDao]).build()) {
val deadbolt: DeadboltActions = implicitApp.injector.instanceOf[DeadboltActions]
val controller = new Subject(deadbolt)
val result: Future[Result] = call(controller.subjectMustBePresent(), FakeRequest())
val statusCode: Int = status(result)
statusCode must be equalTo 401
}
"should result in a 200 when a subject is present" in new WithApplication(new GuiceApplicationBuilder().in(Mode.Test).bindings(bind[SubjectDao].to[TestSubjectDao]).build()) {
val deadbolt: DeadboltActions = implicitApp.injector.instanceOf[DeadboltActions]
val controller = new Subject(deadbolt)
val result: Future[Result] = call(controller.subjectMustBePresent(), FakeRequest().withHeaders(("x-deadbolt-test-user", "greet")))
val statusCode: Int = status(result)
statusCode must be equalTo 200
}
}
}
It doesn't answer exactly to my original question, which was mostly related with Deadbolt2, but I kept getting frustrated with the fact I had to specify my authorization rules in my controllers, which is not truly cross cutting.
The answer provided by Steve Chaloner helps, but still forced me to go through a few hoops.
Enter Panoptes. This authorization framework is based on Filters instead of Action chaining, so it allows to easily specify authorization rules in a central location and outside of the controllers.
Setting your security rules in Panoptes is somewhat similar to Spring Security and it looks like this:
class BasicAuthHandler extends AuthorizationHandler {
override def config: Set[(Pattern, _ <: AuthorizationRule)] = {
Set(
Pattern(Some(POST), "/products") -> atLeastOne(withRole("Admin"), withRole("Manager"))
Pattern(Some(GET), "/cart[/A-Za-z0-9]*") -> withRole("Admin"),
Pattern(None, "/orders[/A-Za-z0-9]*") -> withRole("Admin")
)
}
}
Other than that, you need a couple of lines to declare the filter and plug in your AuthorizationHandler.
class Filters #Inject()(securityFilter: SecurityFilter) extends HttpFilters {
override def filters = Seq(securityFilter)
}
class ControllerProviderModule extends AbstractModule {
override def configure(): Unit = { bind(classOf[AuthorizationHandler]).to(classOf[MyAuthorizationHandler])
}
}
The README file in the git repository has more details and code samples.
It's also customizable to the point it allows to create your own AuthorizationRules. In my project I have a requirement where I need to check the mobile device that makes the call is registered in the system. I can write an AuthorizationRule to handle this for me for every request whose path matches my pattern.
Unit testing controllers is extra simple, because any mocking of the security layer can be ommited. They can be tested like any other class.
If you're having similar issues or also believe authorization rules don't belong in the controllers, have a go at Panoptes, it might suit your needs. Hope this helps someone else.
Below is my controller:
package controllers
import java.util.TimeZone
import akka.actor.{ActorNotFound, ActorSystem}
import akka.util.Timeout
import com.google.inject.Inject
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
import play.api.Logger
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.json.{JsValue, JsError, JsSuccess}
import play.api.mvc._
import scala.concurrent.Future
import scala.concurrent.duration._
class ScheduleController #Inject()(system: ActorSystem) extends Controller {
val scheduler = QuartzSchedulerExtension.get(system)
implicit val timeout = new Timeout(5.seconds)
def index = Action.async {
Future.successful(Ok("hi"))
}
def run = Action.async { request =>
// find actor and "start" it by sending message to it, return Ok() if found
// if actor is not found, return BadRequest()
// if error return InternalServerError()
}
}
I've been looking for tutorials but most are outdated since they deal with Play 2.3.
I solved a very similar problem with Play2 controllers and actors except in my case I'm not injecting an ActorSystem into my controller. Rather, I use Play's default actor system and create actors from my global settings object when my application starts. For a given actor I provide a selection method in its companion object. For example:
class MyActor extends Actor {
def receive = {
case Request => sender ! Response
}
}
object MyActor {
val path = // assuming fixed actor path
val sys = // reference to play actor system
def select: ActorSelection = sys.actorSelection(path)
// my actor messages
case object Request
case object Response
}
The way I have this actor setup I plan on asking it from inside my controller method using a Request and expect to process a Response object back from it. The controller is a non-actor sender in this scenario. By calling ask on the actor selection, I close over the future and map the result to a partial function that matches the expected response. Here's what my controller looks like:
class MyController extends Controller {
implicit val timeout = // set your timeout duration
def index = Action.async {
MyActor.select ? Request map {
case Response => Ok("success")
}
}
}
Testing this controller endpoint is VERY challenging because your actor might not be in a ready state to start handling messages. I spent about two days trying to figure out how to properly block on the my actor's lifecycle state using the actor selection I've exposed through its companion object. What I ended up doing inside my test class that worked for me was to create a method that takes in a function parameter and loops over my actor state until I can resolve its ActorRef. I'm a fan of ScalaTest so this is what my unit tests amounted to:
class MySpec extends FlatSpec with Matchers with BeforeAndAfterAll {
implicit val fakeApp: FakeApplication = new FakeApplication()
override def beforeAll() = Play.start(fakeApp)
override def afterAll() = Play.stop(fakeApp)
def awaitableActorTest(assertions: => Any): Unit = {
val timeout = 1.second
var isActorReady = false
while(!isActorReady) {
val futureRef = Await.ready(MyActor.select.resolveOne(timeout), timeout)
futureRef.value.get match {
case Success(_) =>
assertions
isActorReady = true
case Failure(_) =>
Thread.sleep(2000)
}
}
}
it should "process actor request and response via controller endpoint" in {
awaitableActorTest {
val result = route(routes.MyController.index).get
status(result) shouldBe OK
contentAsString(result) shouldBe "success"
}
}
}
What I get out of this awaitableActorTest pattern is a really clean way of reliably hitting my controller endpoint only when my actor is alive and available to process messages. If it's not, my ActorRef using its selection companion won't resolve in the future with success -- instead it completes with failure. When it fails I sleep a few seconds and repeat the loop. I break the loop when I its selection successfully resolved an ActorRef.
I'm unfamiliar with the QuartzSchedulerExtension to which you refer in your code snippet, but I hope my example is useful.
This is a continuation of a prior question in which answer I saw that I had a misconception about the use of plugins in FakeApplication.
First I have a Model created as a trait and a object that implment it.
trait UserModel extends ModelCompanion[User, ObjectId] {
// ...
}
object User extends UserModel
Next, I have a Controller, created as a abstract class that receive a instance of UserModel, and its respective implementation that uses the User object.
abstract class UsersController extends Controller {
val userModel: UserModel
def sayHello = Action(parse.json) { request =>
// return a play Action. It doesn't use userModel
}
// Other methods
}
object Users extends UsersController(User)
In test directory, I created a UsersController Stub using a UserModel mock:
package controllers
import org.specs2.mock.Mockito
object UserControllersTest extends UsersController with Mockito {
val userModel = mock[models.UserModel]
}
Now I have my UsersControllers Spec test:
package controllers
import org.specs2.mutable.Specification
import play.api.libs.json.Json
import play.api.test._
import play.api.test.Helpers._
class UsersSayHelloSpec extends Specification {
running(FakeApplication()) {
"Users.SayHello" should {
def sendJson(jsonMap: Map[String, String], shouldBeCorrect: Boolean) = {
running(new FakeApplication(
additionalPlugins = Seq("controllers.UserControllersTest"))) {
// Preapration
val jsonRequisition = Json.toJson(jsonMap)
// ***************************************
// It will call UsersControllers.sayHello
val Some(result) = routeAndCall(FakeRequest(POST,
"/hello",
FakeHeaders(Map("Content-Type" -> Seq("application/json"))),
jsonRequisition))
// ***************************************
// ...
}
}
"Not process a empty String" in {
sendJson(Map.empty[String, String], false)
}
// Other tests calling sendJson ...
}
}
}
So my question is: How can I say to FakeApplication to use UserControllersTest instead the real UserControllers implementation when call "/hello" URL, in routeAndCall() calling?
There are two different part of your application which you might want to test:
The controller itself
The http router
When you test the controller itself, you typically create a request and you directly pass it to a controller method, which will generate the answer. You then perform some validation of the answer to verify the test result.
When you test the router, what you want to test is that the request is routed to the right controller method: you directly call the url and you check that the result is the one you expect from the controller you expected.
What you are trying to do does not make much sense to me:
if on route /hello I have a dummyController, will my post to the route
/hello works correctly?
Since the routes file is compiled into a Scala class which actually works as a router, if (and I am not sure) such a dynamic routing feature is available in play, you would have to:
Delete the existing route on the /hello path
Add a new route point to a different controller