Невозможно протестировать контроллер с помощью Action.async
Я пытаюсь проверить контроллер, который использует новый Action.async
. Следуя documentation, я исключил часть под контроллером. Я хочу проверить отдельный признак с типом ссылки:
trait UserController { this: Controller =>
def index() = Action { /* snip */ }
def register() = Action.async(parse.json) { request => /* snip */ }
}
В документации указано, что я должен проверить ее как:
object UsersControllerSpec extends PlaySpecification with Results {
class TestController() extends Controller with UserController
"index action" should {
"should be valid" in {
val controller = new TestController()
val result: Future[SimpleResult] = controller.index().apply(FakeRequest())
/* assertions */
}
}
}
}
Для метода index()
он работает отлично, к сожалению, я не могу сделать то же самое с register()
, так как применение FakeRequest на нем возвращает экземпляр Iteratee[Array[Byte], SimpleResult]
. Я заметил, что у него есть метод run()
, который возвращает Future[SimpleResult]
, но независимо от того, как я строю FakeRequest
, он возвращается с 400
без какого-либо содержимого или заголовков. Мне кажется, что содержание FakeRequest
вообще не учитывается. Должен ли я кормить тело запроса так или иначе, а потом запустить его? Я не мог найти никакого примера, как я мог это сделать.
Ответы
Ответ 1
Для меня это работает:
import concurrent._
import play.api.libs.json._
import play.api.mvc.{SimpleResult, Results, Controller, Action}
import play.api.test._
import ExecutionContext.Implicits.global
trait UserController {
this: Controller =>
def index() = Action {
Ok("index")
}
def register() = Action.async(parse.json) {
request =>
future(Ok("register: " + request.body))
}
}
object UsersControllerSpec extends PlaySpecification with Results {
class TestController() extends Controller with UserController
"register action" should {
"should be valid" in {
val controller = new TestController()
val request = FakeRequest().withBody(Json.obj("a" -> JsString("A"), "b" -> JsString("B")))
val result: Future[SimpleResult] = controller.register()(request)
/* assertions */
contentAsString(result) === """register: {"a":"A","b":"B"}"""
}
}
}
Ответ 2
Эта проблема возникает из-за того, что play.api.mvc.Action[A]
содержит эти два применяемых метода:
// What you're hoping for
def apply(request: Request[A]): Future[Result]
// What actually gets called
def apply(rh: RequestHeader): Iteratee[Array[Byte], Result]
Это возникает из-за того, что Request[A] extends RequestHeader
, а A
в этом случае делает разницу. Если это не правильный тип, вы в конечном итоге вызываете неправильный apply
.
Когда вы используете ActionBuilder
с BodyParser[A]
, вы создаете Action[A]
. В результате вам понадобится Request[A]
для тестирования. parse.json
возвращает a BodyParser[JsValue]
, поэтому вам нужно Request[JsValue]
.
// In FakeRequest object
def apply(): FakeRequest[AnyContentAsEmpty.type]
FakeRequest()
не дает вам тип, который вам нужен. К счастью:
// In FakeRequest class
def withBody[B](body: B): FakeRequest[B]
Итак, начните писать тест, используя заполнитель для тела:
"should be valid" in {
val controller = new TestController()
val body: JsValue = ??? // Change this once your test compiles
// Could do these lines together, but this shows type signatures
val request: Request[JsValue] = FakeRequest().withBody(body)
val result: Future[Result] = controller.index().apply(request)
/* assertions */
}