jbrown
jbrown

Reputation: 7986

How to test a play framework controller that uses a custom bodyparser?

I'm trying to test a play framework 2.5.10 controller that uses a custom bodyparser and JSON validator:

class MessagingController {
  def validateJson[A : Reads] = parse.json.validate(
    _.validate[A].asEither.left.map(e => BadRequest(JsError.toJson(e)))
  )
  def createMessageThread() = Action(validateJson[InboundMessageThread]) { request => 
    Ok("OK") }
  }

When I run a simple test case on this I get an error:

For request 'POST /api/v1/messageThreads' [Invalid Json: No content to map due to 
end-of-input at [Source: akka.util.ByteIterator$ByteArrayIterator$$anon$1@5693d1d2; line: 1, column: 0]]

The test is:

  val fakeRequest = FakeRequest()
      .withHeaders(HeaderNames.CONTENT_TYPE -> "application/json")
      .withBody(Json.parse(
        s"""
           |{
           |  "participants": [
           |   {"id": $currentUserId, "isAdmin": false}
           |  ],
           |  "isGroupThread": false
           |}
    """.stripMargin))

  val result =  messagingController.createMessageThread()(fakeRequest).run()
  status(result) mustBe OK

If I change the controller's action to just Action and then validate the JSON in the body of the controller it works. I don't want to do that though because lots of my REST controllers need to parse JSON and using validateJson[T] reduces boilerplate. The code also works if I submit the same payload using curl.

How can I test this controller?

Upvotes: 1

Views: 1086

Answers (3)

jbrown
jbrown

Reputation: 7986

The following sort of request works:

val request = FakeRequest()
   .withHeaders(CONTENT_TYPE -> "application/json")
   .withBody(Json.fromJson[Place](body).get)

Upvotes: 1

Will Sargent
Will Sargent

Reputation: 4396

Rather than calling the Action with a JSON bodyparser, I'd suggest calling it with an ActionBuilder using an AnyContent body parser and then calling Json.validate from inside the body -- from there, you get a stack trace and see what's failed:

class MessagingController {      
  def createMessageThread() = Action { request => 
    request.json.validate[MessageThread] {
      case JsError(e) =>           
         logger.error(s"Failure parsing because $e")
         BadRequest(JsError.toJson(e))
      case JsSuccess(e) =>      
         Ok("OK")
    }
  }
}

Upvotes: 0

Arpit Suthar
Arpit Suthar

Reputation: 754

Try this, hope this helps

 val postRequest = FakeRequest(POST, "/route/to/hit").withJsonBody(Json.obj(
    "participants" -> Json.arr(Json.obj("id" -> "currentUserId", "isAdmin" -> false)),
    "isGroupThread" -> false
  ))

  val result =  await(messagingController.createMessageThread()(postRequest))

  status(result) must be equalTo (FORBIDDEN)

Upvotes: 0

Related Questions