Play控制器单元测试

时间:2017-10-13 14:38:29

标签: xml scala playframework specs2

我正在编写一个新的Scala Play 2.6 Web服务,并尝试为接受XML有效负载的控制器编写单元测试。但是,当我从单元测试中调用控制器时,我从主体解析器返回400(代码根本没有进入控制器方法体),错误消息显示为For request 'PUT /series/97234752' [Invalid XML: Premature end of file.]。正在解析的XML是有效的,我甚至通过加载一个非常简单的XML字符串来测试它,导致相同的错误。我已经读过这个问题可能与bodysarser尝试从已经读过的流中读取有关,但我无法弄清楚如何正确地执行它(如果这是问题)。

这是单元测试代码:

@RunWith(classOf[JUnitRunner])
class SeriesControllerSpec(implicit ee: ExecutionEnv) extends PlaySpecification with Mockito {
  val coreApi = mock[CoreApiService]
  val app = new GuiceApplicationBuilder().overrides(bind[CoreApiService].toInstance(coreApi)).build

  "SeriesController#save" should {
    "save a valid series in core-api and return the full model upon success" in new WithApplication(app) {
      val body = XML.load(getClass.getResource("/SeriesControllerSpec/valid_series.xml"))
      val coreApiResponse = Series(
        epgContentSourceId = "",
        seriesId = Some("54321"),
        partnerSeriesId = "97234752",
        seriesTexts = Set(
          LocalizedTextField(content = "English Title", field = "title", `type` = "full", lang = "en"),
          LocalizedTextField(content = "Brief English Title", field = "title", `type` = "brief", lang = "en"),
          LocalizedTextField(content = "English Sort Title", field = "title", `type` = "sort", lang = "en"),
          LocalizedTextField(content = "English Description", field = "description", `type` = "full", lang = "en"),
          LocalizedTextField(content = "Brief English Description", field = "description", `type` = "brief", lang = "en"),
          LocalizedTextField(content = "Spanish Title", field = "title", `type` = "full", lang = "es"),
          LocalizedTextField(content = "Brief Spanish Title", field = "title", `type` = "brief", lang = "es"),
          LocalizedTextField(content = "Spanish Sort Title", field = "title", `type` = "sort", lang = "es"),
          LocalizedTextField(content = "Spanish Description", field = "description", `type` = "full", lang = "es"),
          LocalizedTextField(content = "Brief Spanish Description", field = "description", `type` = "brief", lang = "es")
        ),
        genres = Set(Genre(genre = "documentary", `type` = Some("nielsen"))),
        categories = Set(Category(name = "original", `type` = None), Category(name = "Europe > UK > Manchester", `type` = Some("location"))).toSet
      )

      coreApi.saveSeries(any[Series]) returns Future(coreApiResponse, true)

      val controller = app.injector.instanceOf[SeriesController]
      val result = controller.save("97234752")(FakeRequest(method = "PUT", path = "/series/97234752").withHeaders(("Content-Type", "application/xml")).withXmlBody(body))

      val res = Await.result(result.run(), 1 second)

      res must be_===(Created(""))
    }
  }
}

这是控制器的(部分):

class SeriesController @Inject()(coreApi: CoreApiService)(implicit val executionContext: ExecutionContext) extends XmlController {
  def save(seriesId: String) = Action.async(parse.xml) { request =>
    // execution never reaches in here
  }
}

1 个答案:

答案 0 :(得分:1)

问题是您在控制器中使用parse.body。当您打算使用PlayBodyParsers中的一个解析器时,如parse.xml;你必须使用方法play.api.test.Helpers.call来获得结果。以下是实施的示例:

控制器:

package controllers

import javax.inject.{Inject, Singleton}
import play.api.mvc._

import scala.xml.NodeSeq

@Singleton
class XmlController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {

  private final val requestMaxLength: Int = 99999

  def post: Action[NodeSeq] = Action(parse.xml(requestMaxLength)) { implicit request: Request[NodeSeq] =>
    Ok(request.body)
  }
}

测试:

package controllers

import akka.stream.Materializer
import org.scalatest.{FlatSpec, Matchers, OptionValues}
import org.scalatestplus.play.guice._
import play.api.http.{HeaderNames, MimeTypes}
import play.api.mvc.{AnyContentAsXml, ControllerComponents, Request, Result}
import play.api.test.Helpers._
import play.api.test._

import scala.concurrent.Future
import scala.io.Source
import scala.xml.NodeSeq

class XmlControllerTest extends FlatSpec with Matchers with GuiceOneAppPerTest with OptionValues with Injecting with HeaderNames {

  lazy val cc: ControllerComponents = inject[ControllerComponents]

  lazy implicit val materializer: Materializer = app.materializer

  "Post" should "create a valid xml" in {
    val xmlString: String = Source.fromResource("request.xml").getLines().mkString
    val xml: NodeSeq = scala.xml.XML.loadString(xmlString)
    val controller: XmlController = new XmlController(cc)

    val request: Request[AnyContentAsXml] = FakeRequest()
      .withMethod(POST)
      .withHeaders(CONTENT_TYPE -> MimeTypes.XML)
      .withXmlBody(xml)

    val postResult: Future[Result] = call(controller.post, request)

    play.api.test.Helpers.call()

    status(postResult) should equal(OK)
    contentType(postResult).value should equal(MimeTypes.XML)
  }

}

重要的一点是:

  

val postResult:Future [Result] = call(controller.post,request)

我希望这可以解决您的问题。