How to use Status in own code in Play - scala

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) { ....}

Related

How to mock return of None of method which return type is Option[SomeCaseClassDefinedInsideThisClass]

I would expect this test to pass:
import org.scalamock.scalatest.MockFactory
import org.scalatest.{FlatSpec, Matchers}
class FruitImpl {
case class FruitName(name: String)
def getFruitName: Option[FruitName] = {
Some(FruitName("apple"))
}
}
class FruitSpec extends FlatSpec with Matchers with MockFactory {
val f = mock[FruitImpl]
(f.getFruitName _).expects().returning(None)
behavior of "getFruitName method"
it should "return None" in {
f.getFruitName should === (None)
}
}
But it fails with:
[error] my/path/QuestionTest.scala:13: overriding method getFruitName in class FruitImpl of type => Option[this.FruitName];
[error] method getFruitName has incompatible type
[error] val f = mock[FruitImpl]
[error] ^
This works, though:
import org.scalamock.scalatest.MockFactory
import org.scalatest.{FlatSpec, Matchers}
case class FruitName(name: String)
class FruitImpl {
def getFruitName: Option[FruitName] = {
Some(FruitName("apple"))
}
}
class FruitSpec extends FlatSpec with Matchers with MockFactory {
val f = mock[FruitImpl]
(f.getFruitName _).expects().returning(None)
behavior of "getFruitName method"
it should "return None" in {
f.getFruitName should === (None)
}
}
The only difference is that the case class FruitName is defined outside of the class FruitImpl. Why does one version of the code fails and the other doesn't? What should one do to fix the error in the first example?
Without looking at the ScalaMock code, I'd say that the mock is not a true derivation of FruitImpl in the OO sense. Its purpose is to allow method interception, so it only deals with the facade. It follows then that the mock actually has no definition of the path dependent type FruitName, and so cannot work with a method signature that depends on it.
This is precisely why it does work when the FruitName definition is moved out of FruitImpl. It now exists independently of the mock, who's method signatures depending on it then work as expected.

could not find implicit value for parameter messages: play.api.i18n.Messages

I have the following piece of code
import play.api.i18n.{MessagesApi, Messages, I18nSupport}
import play.api.libs.json.Json
case class HttpMessage(key: String, message: String)
object HttpMessage {
implicit val jsonFormat = Json.format[HttpMessage]
def apply(key: String): HttpMessage = {
HttpMessage(key, Messages(key))
}
}
When compiled, it throws
[error] could not find implicit value for parameter messages: play.api.i18n.Messages
[error] HttpMessage(key, messages(key))
[error] ^
I made some research and it seems that it cannot find an implicit value for MessagesAPI. It seems it must be inject like in controllers but I do not know how because I am facing an object and case class here. #Inject annotation is not accepted.
How can I fix this?
Approach from https://stackoverflow.com/a/30843682/4496364 :
import play.api.Play.current
import play.api.i18n.Messages.Implicits._
The first line is deprecated since Play now uses DI everywhere possible.
My approach (can't say if good or bad):
case class HttpMessage(key: String, message: String)
object HttpMessage {
implicit val jsonFormat = Json.format[HttpMessage]
def apply(key: String)(implicit messages: Messages): HttpMessage = {
HttpMessage(key, Messages(key))
}
}
I had to create similar solution, so I used the implicit way, which Play uses also in it's templates. You must have implicit request in your controller for this to work. Also, in all service-like classes you need to forward this implicit messages: Messages...

Why are implicit variables not initialized in Scala when called from unit test?

Given the the following singleton Object in Scala:
package demo
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.concurrent.Future
import scala.io.StdIn
object WebServer extends App {
implicit val system = ActorSystem("myActorSystem")
implicit val executionContext = system.dispatcher
implicit val materializer = ActorMaterializer()
val route = {
path("api" / "done-as-promised") {
get {
complete {
Future.successful("done")
}
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}
And the following unit test
package demo
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.{Inspectors, Matchers, WordSpec}
class WebServerSpec extends WordSpec with Matchers with TypeCheckedTripleEquals with Inspectors with ScalatestRouteTest {
"The WebServer /done-as-promised" should {
"return done" in {
// tests:
Get("/api/done-as-promised") ~> WebServer.route ~> check {
status.intValue() shouldEqual 200
responseAs[String] shouldEqual "done"
}
}
}
}
I get the following error:
[ERROR] [04/19/2016 07:12:18.995]
[ScalaTest-run-running-WebServerSpec]
[akka.actor.ActorSystemImpl(demo-WebServerSpec)] Error during
processing of request
HttpRequest(HttpMethod(GET),http://example.com/api/done-as-promised,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1))
java.lang.NullPointerException at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$1.apply(ExecutionDirectives.scala:33)
at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$1.apply(ExecutionDirectives.scala:29)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:162)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:150)
It took me a while to figure out. The thing is: removing the extends app will make the test succeed.
The reason for the problem is that when WebServer is declared as extends App, it uses the DelayedInit functionality of the App trait. Because of this, the initialization code in the contructor is not added to the constructor of the WebServer object. Instead is called when the main method is called on the WebServer. So when he references the "route" inside the tests, those are all coming up null.
Mixing in the DelayedInit trait (App extends from DelayedInit) will rewrite your class or object template. Instead of adding your val's and var's to the constructor, it will be added to the def delayedInit(body: => Unit) hook (inaccessible to user code). Apparently this one is called whenever the main method is called.
You can verify this by simply calling "main" on the WebServer inside the test. If you do this, then the test will pass. This is because calling main triggers the initialization resulting in those objects being created.
Generally speaking though the right solution is probably to move the routing to somewhere else, rather than having it inside of the base App.

Spray won't convert my case class to json and expect a spray.httpx.marshalling.ToResponseMarshallable

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.

Stub Controller in Play 2.0

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 ?