无法使用Action.async测试控制器

mcv*_*eat 12 scala playframework specs2 playframework-2.2

我正在尝试测试控制器,它正在使用新的Action.async.下面的文档我已经排除了控制器下的部分我想测试用类型引用来分隔特征:

trait UserController { this: Controller =>
  def index() = Action { /* snip */ }
  def register() = Action.async(parse.json) { request => /* snip */ }
}
Run Code Online (Sandbox Code Playgroud)

文档说明我应该将其测试为:

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 */
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

对于index()方法它完美地工作,不幸的是我无法做同样的事情register(),因为在它上面应用FakeRequest返回的实例Iteratee[Array[Byte], SimpleResult].我注意到它有run()返回的方法,Future[SimpleResult]但无论我如何构建FakeRequest它返回400没有任何内容或标题.在我看来,内容完全FakeRequest被忽视了.我应该以某种方式将请求主体提供给iteratee然后运行它吗?我找不到任何例子我怎么能这样做.

tjd*_*ett 8

出现此问题是因为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]
Run Code Online (Sandbox Code Playgroud)

出现这种情况是因为Request[A] extends RequestHeader,A在这种情况下会产生重大影响.如果它不是正确的类型,你最终会说错了apply.

当您使用ActionBuildera时BodyParser[A],您创建一个Action[A].因此,您需要Request[A]进行测试.parse.json返回一个BodyParser[JsValue],所以你需要一个Request[JsValue].

// In FakeRequest object
def apply(): FakeRequest[AnyContentAsEmpty.type]
Run Code Online (Sandbox Code Playgroud)

FakeRequest()不能为您提供所需的类型.幸好:

// In FakeRequest class
def withBody[B](body: B): FakeRequest[B]
Run Code Online (Sandbox Code Playgroud)

因此,通过使用正文的占位符开始编写测试:

  "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 */
  }
Run Code Online (Sandbox Code Playgroud)


Sch*_*rdt 6

对我来说,这是:

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"}"""
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 关于这个问题,以及@tjdett的评论,更多的是`withJsonBody`返回一个`FakeRequest [AnyContentAsJson]`,而不是'FakeRequest [JsValue]`.所以不要使用`withJsonBody`,使用`withBody`. (5认同)