I am trying folow the example from Mock Objects in Play[2.0] but unfortunately I am not having success.
I have a UsersController that uses a UserModel.
trait UserModel extends ModelCompanion[User, ObjectId] {
// ...
}
Next, the abstract controller
abstract class UsersController extends Controller {
val userModel: UserModel
def sayHello = Action(parse.json) { request =>
// return a play Action. Doesn't use userModel
}
// Other methods
}
In the routes file, I call method say Hello in this way:
POST /hello controllers.Users.sayHello
In test directory, I created a subclass of UsersController using a UserModel mock.
package controllers
import org.specs2.mock.Mockito
object UserControllersTest extends UsersController with Mockito {
val userModel = mock[models.UserModel]
}
Now, the main part. I created a Spec test following the Jacob Groundwater example in the page mentioned before. In pluing argument for FakeApplication, I included a calling to UserControllersTest.
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)
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 ...
}
}
}
However, when I run the test I got this error message:
[info] Users.SayHello should
[error] ! Not process a empty String
[error] PlayException: Cannot load plugin [Plugin [controllers.UserControllersTest] cannot been instantiated.] (Application.scala:171)
...
[error] play.api.Application.<init>(Application.scala:158)
[error] play.api.test.FakeApplication.<init>(Fakes.scala:141)
[error] controllers.UsersSayHelloSpec$$anonfun$1$$anonfun$apply$5.sendJson$1(UsersSayHelloSpec.scala:20)
[error] controllers.UsersSayHelloSpec$$anonfun$1$$anonfun$apply$5$$anonfun$apply$26.apply(UsersSayHelloSpec.scala:46)
[error] controllers.UsersSayHelloSpec$$anonfun$1$$anonfun$apply$5$$anonfun$apply$26.apply(UsersSayHelloSpec.scala:46)
Where UsersSayHelloSpec.scala:20 referes to line where I call running method.
So my question is: What am I doing wrong?
I'm not sure what exactly are you trying to do, but the answer for question 'What am I doing wrong?' is:
The parameter 'additionalPlugins' is for additional Play plugins, and 'controllers.UserControllersTest' is not a Play plugin. It's a Controller.
You can read about Play 2 plugins here: http://www.objectify.be/wordpress/?p=464
Have you tried these examples: http://www.playframework.org/documentation/2.0.4/ScalaFunctionalTest ?
Related
Playversion is 2.6.12.
I want to use Results.Status in my own code like
// in my code somewhere
def doSomething(status:Results.Status) { ....}
// in controller
doSomething(Ok) <---- results in error
But I the Ok inside a controller seems not to be of type Results.Status
[error] found : MyController.this.Status
[error] required: play.api.mvc.Results.Status
Any ideas how to use Status in my own code outside controllers?
The helper code is
import org.scalactic.{Bad, Good, Or}
import play.api.libs.json.Json
import play.api.mvc.Result
import play.api.mvc.Results.Status
object Helper {
def toResult[T](r:Or[T, Result], s:Status):Result = {
r match {
case Good(entity) => s(Json.toJson(entity))
case Bad(badRequest) => badRequest
}
}
}
The controller code definition is extending
class AuthBaseController #Inject()(acc: AuthControllerComponents) extends BaseController with AuthRequestMarkerContext {
where BaseController leads to
trait ControllerHelpers extends Results with HttpProtocol with Status with HeaderNames with ContentTypes with RequestExtractors with Rendering with RequestImplicits
extending Status.
You can always explicitly pass the requested type:
doSomething(play.api.mvc.Results.Ok)
I suppose if you just import play.api.mvc.Results._ in your controller, you will have an import conflict (you can still try it), but just importing play.api.mvc.Results, and then using it like this
doSomething(Results.Ok)
should work.
[error] found : MyController.this.Status
[error] required: play.api.mvc.Results.Status
Status is a specific type that depends on the controller. To have a function that's a certain type, that doesn't care about the parent type you can use the # symbol like this:
def doSomething(status:Results#Status) { ....}
I'm looking for a way to inject a dependency into a Test (in /tests/models/) that looks like following:
class FolderSpec(implicit inj: Injector) extends Specification with Injectable{
val folderDAO = inject [FolderDAO]
val user = User(Option(1), LoginInfo("key", "value"), None, None)
"Folder model" should {
"be addable to the database" in new WithFakeApplication {
folderDAO.createRootForUser(user)
val rootFolder = folderDAO.findUserFolderTree(user)
rootFolder must beSome[Folder].await
}
}
}
Where
abstract class WithFakeApplication extends WithApplication(FakeApplication(additionalConfiguration = inMemoryDatabase()))
/app/modules/WebModule:
class WebModule extends Module{
bind[FolderDAO] to new FolderDAO
}
/app/Global:
object Global extends GlobalSettings with ScaldiSupport with SecuredSettings with Logger {
def applicationModule = new WebModule :: new ControllerInjector
}
But at compilation time I have following stack trace:
[error] Could not create an instance of models.FolderSpec
[error] caused by java.lang.Exception: Could not instantiate class models.FolderSpec: argument type mismatch
[error] org.specs2.reflect.Classes$class.tryToCreateObjectEither(Classes.scala:93)
[error] org.specs2.reflect.Classes$.tryToCreateObjectEither(Classes.scala:207)
[error] org.specs2.specification.SpecificationStructure$$anonfun$createSpecificationEither$2.apply(BaseSpecification.scala:119)
[error] org.specs2.specification.SpecificationStructure$$anonfun$createSpecificationEither$2.apply(BaseSpecification.scala:119)
[error] scala.Option.getOrElse(Option.scala:120)
Sadly, I didn't find anything on the matter in Scaldi documentation.
Is there a way to inject things in tests?
Scaldi does not provide an integration with any testing framework, but you actually normally don't need it. What you can do in this case is to create a test Module that contains mocks and stubs (like in-memory databases) and then just provide a test Global to the FakeApplication. Here is an example of how you can do it:
"render the index page" in {
class TestModule extends Module {
bind [MessageService] to new MessageService {
def getGreetMessage(name: String) = "Test Message"
}
}
object TestGlobal extends GlobalSettings with ScaldiSupport {
// test module will override `MessageService`
def applicationModule = new TestModule :: new WebModule :: new UserModule
}
running(FakeApplication(withGlobal = Some(TestGlobal))) {
val home = route(FakeRequest(GET, "/")).get
status(home) must equalTo(OK)
contentType(home) must beSome.which(_ == "text/html")
println(contentAsString(home))
contentAsString(home) must contain ("Test Message")
}
}
You can find this code in the scaldi-play-example application.
I'm trying to reprocude this or this, but I keep getting an error I am not able to fix...
First of all, here are my dependencies:
compile 'io.spray:spray-can_2.11:1.3.1'
compile 'io.spray:spray-routing_2.11:1.3.1',
compile 'io.spray:spray-json_2.11:1.2.6'
Now what I'm trying to do is:
class WHttpService extends Actor with HttpService with ActorLogging {
implicit def actorRefFactory = context
def receive = runRoute(route)
lazy val route = logRequest(showReq _) {
// Way too much imports but I tried all I could find
import spray.json._
import DefaultJsonProtocol._
import MasterJsonProtocol._
import spray.httpx.SprayJsonSupport._
path("server" / Segment / DoubleNumber / DoubleNumber) { (login, first, second) =>
get {
complete {
Answer(1, "test")
}
}
}
}
private def showReq(req : HttpRequest) = LogEntry(req.uri, InfoLevel)
}
With:
case object MasterJsonProtocol extends DefaultJsonProtocol with SprayJsonSupport {
import spray.json._
case class Answer(code: Int, content: String)
implicit val anwserFormat: JsonFormat[Answer] = jsonFormat2(Answer)
}
Now I get this error:
Error:(42, 19) type mismatch;
found : MasterJsonProtocol.Answer
required: spray.httpx.marshalling.ToResponseMarshallable
Answer(1, "test")
^
I tried a lot of things but can't manage to make it works.
I tried with
Answer(1, "test").toJson
Answer(1, "test").toJson.asJsObject
Finally what I did was
complete {
Answer(1, "test").toJson.compactPrint
}
This works but it is sent to the client as Content-Type: text/plain when I need application/json.
Anyone see what the problem is here?
Edit: I added a sample project on github https://github.com/ydemartino/spray-test
Move your model outside of the json protocol and make it a regular object (not a case object)
case class Answer(code: Int, content: String)
object MasterJsonProtocol extends DefaultJsonProtocol {
implicit val anwserFormat = jsonFormat2(Answer)
}
Edit
Also clean up your imports:
class WHttpService extends Actor with HttpService with ActorLogging {
implicit def actorRefFactory = context
def receive = runRoute(route)
lazy val route = logRequest(showReq _) {
// Way too much imports but I tried all I could find
import MasterJsonProtocol._
import spray.httpx.SprayJsonSupport._
path("server" / Segment / DoubleNumber / DoubleNumber) { (login, first, second) =>
get {
complete {
Answer(1, "test")
}
}
}
}
private def showReq(req : HttpRequest) = LogEntry(req.uri, InfoLevel)
}
I created a pull request to fix your problem: https://github.com/ydemartino/spray-test/pull/1
The json protocol object has to be declared before it can be used implicitly. I'm not wholly sure why the compiler can't figure it out, but moving the object declaration to the top fixed it.
For your actual project make sure to declare packages in each file then use those packages to in the import statements.
In my case the name of the unresolvable implicit format instance conflicted with a local definition, so it got shadowed. The compiler was graciously silent about that. Only discovered that by accident after hours of head-banging.
I am using Play framework 2.2.x and SecureSocial and for testing I am trying to override a method in a superclass. Actually I am getting a compile error using the test helper classes from secure social and I can not figure it out as I am not all that familiar with Scala.
What I don't understand is why my ide things the method is correctly overridden but when I compile I get the error. I know scala is not that well integrated with the ides but I can not see why the override is incorrect.
/**
* This is the secure social class and method I am trying to override.
I have left the other methods out for clarity. The problem method is the doAuth method
**/
package securesocial.core
import providers.utils.RoutesHelper
import play.api.mvc.{SimpleResult, AnyContent, Request}
import play.api.{Play, Application, Plugin}
import concurrent.{Await, Future}
import play.api.libs.ws.Response
abstract class IdentityProvider(application: Application) extends Plugin with Registrable {
/**
* Subclasses need to implement the authentication logic. This method needs to return
* a User object that then gets passed to the fillProfile method
*
* #param request
* #return Either a Result or a User
* This is the method I am having trouble trying to override
*/
def doAuth()(implicit request: Request[AnyContent]):Either[SimpleResult, SocialUser]
}
This is the my class and the doAuth() override
package testkit
import play.api.Logger
import securesocial.core._
import play.api.mvc.{Result, Request}
import securesocial.core.IdentityId
class AlwaysValidIdentityProvider(app:play.api.Application) extends IdentityProvider(app){
val logger = Logger("securesocial.stubs.AlwaysValidIdentityProvider")
def authMethod: AuthenticationMethod = AuthenticationMethod("naive")
override def doAuth()(implicit request: Request[play.api.mvc.AnyContent]): Either[Result, SocialUser] ={
val userId = request.body.toString
val r =Right(SocialUserGenerator.socialUserGen(IdentityId(userId, id), authMethod).sample.get)
r
}
def fillProfile(user: SocialUser): SocialUser = {
user
}
def id: String = "naive"
}
The error I am getting is: As you can see it thinks I am overriding nothing.
[error] /Users/zola/Development/play/receipt-manager/rm-play/test/testkit/AlwaysValidIdentityProvider.scala:8: class AlwaysValidIdentityProvider needs to be abstract, since method doAuth in class IdentityProvider of type [A]()(implicit request: play.api.mvc.Request[A])Either[play.api.mvc.Result,securesocial.core.SocialUser] is not defined
[error] class AlwaysValidIdentityProvider(app:play.api.Application) extends IdentityProvider(app){
[error] ^
[error] /Users/zola/Development/play/receipt-manager/rm-play/test/testkit/AlwaysValidIdentityProvider.scala:13: method doAuth overrides nothing.
[error] Note: the super classes of class AlwaysValidIdentityProvider contain the following, non final members named doAuth:
[error] def doAuth[A]()(implicit request: play.api.mvc.Request[A]): Either[play.api.mvc.Result,securesocial.core.SocialUser]
[error] override def doAuth()(implicit request: Request[play.api.mvc.AnyContent]): Either[Result, SocialUser] ={
[error] ^
[error] two errors found
That's because the method you are overriding is not already defined. You have a signature in the abstract class and that's it.
Try this instead:
def doAuth()(implicit request: Request[play.api.mvc.AnyContent]): Either[Result, SocialUser] ={
val userId = request.body.toString
val r =Right(SocialUserGenerator.socialUserGen(IdentityId(userId, id), authMethod).sample.get)
r
}
To solve the issue I looked at other classes within SecureSocial and they way they implemented the method was
def doAuth[A]()(implicit request: Request[A]): Either[Result, SocialUser] = {
val userId = request.body.toString
val r =Right(SocialUserGenerator.socialUserGen(IdentityId(userId, id), authMethod).sample.get)
r
}
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