Недопустимый Json: нет содержимого для сопоставления из-за конца ввода при использовании анализатора тела воспроизведения

Я пытаюсь протестировать метод контроллера, который пытается проанализировать JSON, отправленный в запросе:

  def addRoomToProfileForTime = Action.async(parse.json[AddRoomToProfileForTimeRequest]) { request =>
    profileService.addRoomToProfileForTime(request.body.roomId, request.body.profileId, request.body.timeRange).value.map {
      case Xor.Right(_)  => Ok
      case Xor.Left(err) => BadRequest(Json.toJson(err))
    }
  }

это класс case, который представляет запрос:

final case class AddRoomToProfileForTimeRequest(
  roomId: Int,
  profileId: Int,
  timeRange: TimeRange
)

implicit val addRoomToProfileForTimeRequestFormat:Format[AddRoomToProfileForTimeRequest] = Json.format

этот код работает так, как ожидалось, я делаю запрос так:

curl -H "Content-Type: application/json" -X POST -d '{"roomId":3,"profileId":1,"timeRange":{"id":1,"fromTime":"2000-01-01T01:01","toTime":"2000-01-01T02:01"}}' http://localhost:9000/api/profiles/addRoomToProfileForTime

но я пытаюсь написать тест для этого метода (обратите внимание, что я использую macwire для инъекции зависимостей, поэтому не могу использовать WithApplication:

"add a room to profile for time" in new TestContext {
  val roomId = 1
  val profileId = 1
  val from = "2000-01-01T01:01"
  val to = "2000-01-01T02:01"
  val requestJson = Json.obj(
    "roomId"    -> roomId,
    "profileId" -> profileId,
    "timeRange" -> Json.obj(
      "id"       -> 1,
      "fromTime" -> from,
      "toTime"   -> to
    )
  )

  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()

  val fakeReq = FakeRequest(Helpers.POST, "api/profiles/addRoomToProfileForTime")
    .withHeaders(CONTENT_TYPE -> "application/json")
    .withJsonBody(requestJson)
  val result = profileController.addRoomToProfileForTime()(fakeReq).run

  val content  = contentAsString(result)
  println(content)
  status(result) must equalTo(OK)
}

тем не менее, этот тест завершается неудачей с плохим запросом от Играть:

<body>                                             
    <h1>Bad Request</h1>                                

    <p id="detail">
        For request 'POST api/profiles/addRoomToProfileForTime' [Invalid Json: No content to map due to end-of-input at [Source: akka.util.ByteIterator$ByteArrayIterator$$anon@37d14073; line: 1, column: 0]]
    </p>

</body>

если я разбираю JSON с request.body.asJson метод ведет себя так, как ожидалось. Я получаю эту ошибку только с помощью метода синтаксического анализа тела выше.

1 ответов


короткий ответ: на ваш FakeRequest в тесте контроллера используйте withBody вместо withJsonBody.

у меня тоже была эта проблема, и я смущенно потратил на нее часы, пока не понял. Длинный ответ таков:FakeRequest ' s withJsonBody возвращает FakeRequest[AnyContentAsJson], и так как ваш контроллер ожидает JsValue (не an AnyContentAsJson), когда вы называете apply() на вашем действии он не соответствует этому методу apply, который вы хочу:

def apply(request: Request[A]): Future[Result]

и вместо хиты это применить метод:

def apply(rh: RequestHeader): Accumulator[ByteString, Result]

и, таким образом, поскольку вы не передаете байты в аккумулятор, вы получаете неожиданное end-of-input сообщение об ошибке вы получаете.