I would like to know what's the best way to run specs2 tests on a PlayFramework module and be able to simulate it running.
My module contains some routes in a file named mymodule.routes
In my apps I integrate them by adding the following line in my routes file
-> /mymodule mymodule.Routes
This is the test from my module I try to run but returns a 404 error :
"test myroute" in {
running(FakeApplication()) {
await(WS.url("http://localhost:9000/mymodule/myroute").get).status must equalTo(OK)
}
}
FakeApplication does not really lunch a web process, so you cannot test using http access to localhost.
You have three options:
Testing the controller directly
Testing the router
Testing the whole app.
Testing the controller is done by directly calling your controller and checking the result, as suggested in play documentation, and providing a FakeRequest()
val result = controllers.Application.index("Bob")(FakeRequest())
Testing the router is done by calling routeAndCall with a FakeRequest argument, specifying the relative path:
val Some(result) = routeAndCall(FakeRequest(GET, "/Bob"))
Eventually, if you want to test your whole application, you need to start a TestServer:
"run in a server" in {
running(TestServer(3333)) {
await(WS.url("http://localhost:3333").get).status must equalTo(OK)
}
}
Your question says: "What is the best option?". The answer is: there is nothing such as a best option, there are different way of testing for different purpose. You should pick up the testing strategy which better fits your requirement. In this case, since you want to test the router, I suggest you try approach n.2
Related
I have a .Net Core web API solution called ReportService, which calls another API endpoint (we can call this PayrollService) to get payroll reports. So my requirement is to mock the PayrollService using Wiremock.Net.
Also currently I have a automation test case written, which will directly call the ReportService controller and will execute all the service logic, and also classes which calls PayrollService and the DB layer logic and will get the HTTP result back from the ReportService.
Please note that the Automation test cases is a separate solution. So my requirement is to run the automation test cases like before on ReportService, and the payroll service will be mocked by Wiremock.
So, what are the changes that need to happen in the codebase? Do we have to change the url of the ReportService to be the Wiremock server base url in the ReportService solution? Please let us know, and please use the terms I have used in the question regarding the project names so I am clear.
Your assumption is indeed correct, you have make the base URL which is used by ReportService configurable.
So that for your unit / integration tests you can provide the URL on which the WireMock.Net server is running.
Example:
[Test]
public async Task ReportService_Should_Call_External_API_And_Get_Report()
{
// Arrange (start WireMock.Net server)
var server = WireMockServer.Start();
// Setup your mapping
server
.Given(Request.Create().WithPath("/foo").UsingGet())
.RespondWith(
Response.Create()
.WithStatusCode(200)
.WithBody(#"{ ""msg"": ""Hello world!"" }")
);
// Act (configure your ReportService to connect to the URL where WireMock.Net is running)
var reportService = new ReportService(server.Urls[0]});
var response = reportService.GetResport();
// Assert
Assert.Equal(response, ...); // Verify
}
I'm developing a scala application with play frame work, i have created a filter that filters every request coming from outside server,but now i'm stuck on how can i run a filter on demand since two days,i have 80 APIs 30 of them needs to run a specific filter, how can i read the request route template while the requests like this
GET /api/v1/:locale/:uuid core.controllers.MyClass.myAction1(locale: String)
GET /api/v1/:locale/:uuid/MyRoute core.controllers.MyClass.myAction2(locale: String)
GET /api/v1/:locale/:uuid/Foo core.controllers.MyClass.myAction3(locale: String)
GET /api/v1/:locale/orders/:orderId core.controllers.MyClass.myAction4(locale: String)
well, those routes are placed in routes file,
in filter i need to check weather if the route has :uuid variable or :orderId in order to run its specific filter, because both of their ids, i getting them as uuid so i couldn't expect the request, could i read the route template ?
You can access to some routing information from the RequestHeader#attrs:
// in your filter
val handlerDef: Option[HandlerDef] = request.attrs.get(Router.Attrs.HandlerDef)
See HandlerDef api
If you want to choose 30 out of 80 actions to run some common logic, you could also consider using "action builders" to provide that logic.
When you use Action { ... } you get a vanilla action. You can also make your own MyAction { ... } that wraps a normal Action and runs custom logic. This is an ActionBuilder. If you use this approach you just need to update your 30 actions to use that custom action builder.
See: https://www.playframework.com/documentation/2.6.x/ScalaActionsComposition#Custom-action-builders
I am pretty sure that this is a config problem, so I'll post my code and the relevant application.conf options of my play app.
I have a play server that needs to interact with another server "B" (basically multi-file upload to B). The interaction happens inside an async -Action which should result in an OK with B's response on the upload. This is the reduced code:
def authenticateAndUpload( url: String) = Action.async( parse.multipartFormData) { implicit request =>
val form = authForm.bindFromRequest.get
val (user, pass) = (form.user, form.pass)
//the whole following interaction with the other server happens in a future, i.e. login returns a Future[Option[WSCookie]] which is then used
login(user, pass, url).flatMap {
case Some(cookie) => //use the cookie to upload the files and collect the result, i.e. server responses
//this may take a few minutes and happens in yet another future, which eventually produces the result
result.map(cc => Ok(s"The server under url $url responded with $cc"))
case None =>
Future.successful(Forbidden(s"Unable to log into $url, please go back and try again with other credentials."))
}
}
I am pretty sure that the code itself works since I can see my server log which nicely prints B's responses every few seconds and proceeds until everything is correctly uploaded. The only problem is that the browser hangs up with a server overloaded message after 120s which should be a play default value - but for which config parameter?
I tried to get rid of it by setting every play.server.http. timeout option I could get my hands on and even decided to use play.ws, specific akka, and other options of which I am quite sure that they are not necessary... however the problem remains, here is my current application.config part:
ws.timeout.idle="3600s"
ws.timeout.request ="3600s"
ws.timeout.response="3600s"
play.ws.timeout.idle="3600s"
play.ws.timeout.request="3600s"
play.ws.timeout.response="3600s"
play.server.http.connectionTimeout="3600s"
play.server.http.idleTimeout="3600s"
play.server.http.requestTimeout="3600s"
play.server.http.responseTimeout="3600s"
play.server.http.keepAlive="true"
akka.http.host-connection-pool.idle-timeout="3600s"
akka.http.host-connection-pool.client.idle-timeout= "3600s"
The browser hang up happened both on Safari and Chrome, where Chrome additionally started a second communication with B after about 120 seconds - also both of these communications succeeded and produced the expected logs, only the browsers had both hang up.
I am using Scala 2.12.2 with play 2.6.2 in an SBT environment, the server is under development, pre-compiled but then started via run - I read that it may not pick up the application.conf options - but it did on some file size customizing. Can someone tell me the correct config options or my mistake on the run process?
Using the Play 2.2.3 framework, given I have a route like this one in my routes file :
GET /event controllers.Event.events
How can I programatically get the "/event" knowing the method I am trying to reach, in this case 'controllers.Authentication.authenticate' ?
I need it for test purpose, because I would like to have better test waiting not for the route itself but for the controllers method really called. So maybe there is another solution than getting the route and test it this way like I do for now :
//Login with good password and login
val Some(loginResult) = route(FakeRequest(POST, "/login").withFormUrlEncodedBody(
("email", email)
, ("password", password)))
status(loginResult)(10.seconds) mustBe 303
redirectLocation(loginResult).get mustBe "/event"
You construct the reverse route in this way:
[full package name].routes.[controller].[method]
In your example:
controllers.routes.Authentication.authenticate
controllers.routes.Events.events
But say you broke your packages out like controllers.auth and controllers.content, then they would be:
controllers.auth.routes.Authentication.authenticate
controllers.content.routes.Events.events
The reverse routes return a Call, which contains an HTTP method and URI. In your tests you can construct a FakeRequest with a Call:
FakeRequest(controllers.routes.Authentication.authenticate)
And you can also use it to test the redirect URI:
val call: Call = controllers.routes.Events.events
redirectLocation(loginResult) must beSome(call.url)
My web application will be triggered from an external system. It will call one request path of my app, but uses different query parameters for different kinds of requests.
One of the parameters is the "action" that defines what is to be done. The rest of the params depend on the "action".
So I can get request params like these:
action=sayHello&user=Joe
action=newUser&name=Joe&address=xxx
action=resetPassword
...
I would like to be able to encode it similarly in the routes file for play so it does the query param based routing and as much of the validation of other parameters as possible.
What I have instead is one routing for all of these possibilities with plenty of optional parameters. The action processing it starts with a big pattern match to do dispatch and parameter validation.
Googling and checking SO just popped up plenty of samples where the params are encoded in the request path somehow, so multiple paths are routed to the same action, but I would like the opposite: one path routed to different actions.
One of my colleagues said we could have one "dispatcher" action that would just redirect based on the "action" parameter. It would be a bit more structured then the current solution, but it would not eliminate the long list of optional parameters which should be selectively passed to the next action, so I hope one knows an even better solution :-)
BTW the external system that calls my app is developed by another company and I have no influence on this design, so it's not an option to change the way how my app is triggered.
The single dispatcher action is probably the way to go, and you don't need to specify all of your optional parameters in the route. If action is always there then that's the only one you really need.
GET /someRoute controller.dispatcher(action: String)
Then in your action method you can access request.queryString to get any of the other optional parameters.
Note: I am NOT experienced Scala developer, so maybe presented snippets can be optimized... What's important for you they are valid and working.
So...
You don't need to declare every optional param in the routes file. It is great shortcut for type param's validation and best choice would be convince 'other company' to use API prepared by you... Anyway if you haven't such possibility you can also handle their requests as required.
In general: the dispatcher approach seems to be right in this place, fortunately you don't need to declare all optional params in the routes and pass it between actions/methods as they can be fetched directly from request. In PHP it can be compared to $_GET['action'] and in Java version of Play 2 controller - DynamicForm class - form().bindFromRequest.get("action").
Let's say that you have a route:
GET /dispatcher controllers.Application.dispatcher
In that case your dispatcher action (and additional methods) can look like:
def dispatcher = Action { implicit request =>
request.queryString.get("action").flatMap(_.headOption).getOrElse("invalid") match {
case "sayHello" => sayHelloMethod
case "newUser" => newUserMethod
case _ => BadRequest("Action not allowed!")
}
}
// http://localhost:9000/dispatcher?action=sayHello&name=John
def sayHelloMethod(implicit request: RequestHeader) = {
val name = request.queryString.get("name").flatMap(_.headOption).getOrElse("")
Ok("Hello " + name )
}
// http://localhost:9000/dispatcher?action=newUser&name=John+Doe&address=john#doe.com
def newUserMethod(implicit request: RequestHeader) = {
val name = request.queryString.get("name").flatMap(_.headOption).getOrElse("")
val address = request.queryString.get("address").flatMap(_.headOption).getOrElse("")
Ok("We are creating new user " + name + " with address " + address)
}
Of course you will need to validate incoming types and values 'manually', especially when actions will be operating on the DataBase, anyway biggest part of your problem you have resolved now.