【问题标题】:Unable to test controller using Action.async无法使用 Action.async 测试控制器
【发布时间】:2013-10-27 11:08:32
【问题描述】:

我正在尝试测试使用新的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] 的实例。我注意到它具有返回Future[SimpleResult]run() 方法,但无论我如何构建FakeRequest,它都会返回400,没有任何内容或标题。在我看来,FakeRequest 的内容完全被忽略了。我是否应该以某种方式将请求正文提供给 iteratee 然后运行它?我找不到任何示例,我该怎么做。

【问题讨论】:

  • 这个运气好吗?快把我逼疯了!

标签: scala playframework specs2 playframework-2.2


【解决方案1】:

出现这个问题是因为play.api.mvc.Action[A]包含这两个apply方法:

// 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 RequestHeaderA 在这种情况下产生了很大的不同。如果不是正确的类型,你最终会调用错误的apply

当您将ActionBuilderBodyParser[A] 一起使用时,您将创建一个Action[A]。因此,您需要Request[A] 进行测试。 parse.json 返回一个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 */
  }

【讨论】:

    【解决方案2】:

    对我来说是这样的:

    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"}"""
        }
      }
    }
    

    【讨论】:

    • 我的安全操作遇到了类似的问题,是否有类似的解决方案? stackoverflow.com/questions/26303705/…
    • 关于这一点的一个问题,以及@tjdett 的 cmets,是 withJsonBody 返回一个 FakeRequest[AnyContentAsJson],而不是 FakeRequest[JsValue]。所以不要使用withJsonBody,使用withBody
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    相关资源
    最近更新 更多