单元测试播放具有json解析器的控制器操作

时间:2017-09-18 23:17:27

标签: json scala unit-testing playframework

我有一个控制器动作,我想测试但是我收到下面的错误。我觉得有些东西我可能会丢失但却无法弄清楚它是什么。我写了一些小代码来帮助说明问题。

增加更多上下文,我希望能够向控制器提供模拟依赖项,这就是我为什么要manaully接管它的实例化。

java.lang.UnsupportedOperationException: NoMaterializer does not provide an ExecutionContext
[info]   at play.api.test.NoMaterializer$.executionContext(Helpers.scala:661)
[info]   at play.api.mvc.PlayBodyParsers.$anonfun$enforceMaxLength$1(BodyParsers.scala:866)
[info]   at akka.stream.impl.Compose.apply(TraversalBuilder.scala:164)
[info]   at akka.stream.impl.PhasedFusingActorMaterializer.materialize(PhasedFusingActorMaterializer.scala:488)
[info]   at akka.stream.impl.PhasedFusingActorMaterializer.materialize(PhasedFusingActorMaterializer.scala:424)
[info]   at akka.stream.impl.PhasedFusingActorMaterializer.materialize(PhasedFusingActorMaterializer.scala:415)
[info]   at akka.stream.scaladsl.RunnableGraph.run(Flow.scala:496)

以下是我为帮助说明问题而编写的控制器

case class Location(lat: Double, long: Double)

object Location {

  implicit val locationReads: Reads[Location] = (
    (JsPath \ "lat").read[Double](min(-90.0) keepAnd max(90.0)) and
      (JsPath \ "long").read[Double](min(-180.0) keepAnd max(180.0))
    ) (Location.apply _)
}

class HomeController @Inject()(
    cc: ControllerComponents,
    mat: Materializer
)(implicit val ec: ExecutionContext) extends AbstractController(cc) {


  def savePlace: Action[JsValue] = Action(parse.json) { request: Request[JsValue] =>
    val placeResult = request.body.validate[Location]
    placeResult.fold(
      errors => {
        BadRequest(Json.obj("status" -> "KO", "message" -> JsError.toJson(errors)))
      },
      place => {
        Ok(Json.obj("status" -> "OK", "message" -> "Location saved"))
      }
    )
  }
}

和测试代码

class HomeControllerSpec extends PlaySpec with Injecting with Inside with GuiceOneAppPerSuite with BeforeAndAfterEach with Results with BeforeAndAfterAll {
  implicit lazy override val app: play.api.Application = new GuiceApplicationBuilder().configure().build()
  implicit lazy val materializer: Materializer = app.materializer
  implicit lazy val components = Helpers.stubControllerComponents()

  "savePlace" must {

    val api = new HomeController(components, mat = materializer)

    val payload = Json.obj(
      "location" -> Json.obj("lat" -> 51.235685, "lng" -> -1.309197)
    )

    "just demo" in {
      val request = FakeRequest(POST, "/location").withJsonBody(payload)

      val result = call(api.savePlace, request)
      status(result) mustBe UNAUTHORIZED
    }
  }
}

我正在使用play 2.6.3

3 个答案:

答案 0 :(得分:2)

我正在回答这个问题,以帮助可能面临同样问题的其他人。

要解决此问题,我使用Helpers.stubControllerComponents()而不是注入存根控制器组件(如inject[ControllerComponents]中所述)。

基本上,为了解决问题,控制器代码现在看起来像这样。

class HomeControllerSpec
  extends PlaySpec with Injecting with Inside with GuiceOneAppPerSuite with Results {
  implicit lazy override val app: play.api.Application = new GuiceApplicationBuilder().configure().build()
  implicit lazy val materializer = app.materializer

    implicit lazy val components = inject[ControllerComponents]

  "savePlace" must {

    val api = new HomeController(components)

    val payload = Json.obj("lat" -> 51.235685, "long" -> -1.309197)

    "just demo" in {
      val request = FakeRequest(POST, "/location").withJsonBody(payload)

      val result = call(api.savePlace, request)
      println(contentAsJson(result))
      status(result) mustBe UNAUTHORIZED

    }
  }
}

答案 1 :(得分:0)

在测试类中使用GuiceOneAppPerSuite扩展playspec。现在,您将拥有一个可用于对此进行单元测试的应用实例。

E.g。

class HomeSpec extends PlaySpec with GuiceOneAppPerSuite {

  "Home" should {

    "send 404 on a bad request" in {
      route(app, FakeRequest(GET, "/boum")).map(status(_)) mustBe Some(
        NOT_FOUND)
    }

    "send 200 on a good request" in {
      route(app, FakeRequest(GET, "/")).map(status(_)) mustBe Some(OK)
    }

  }
}

我目前正在使用此测试来玩2.6.1。这将是什么,是,您将拥有整个应用程序的实例,因此您可以测试所有控制器,而无需逐个实例化。

答案 2 :(得分:0)

在某些情况下,您不希望(或不能)使用依赖项注入。

错误来自stubControllerComponents具有playBodyParsers: PlayBodyParsers = stubPlayBodyParsers(NoMaterializer)的事实,因此将上面的components = ...更改为:

implicit lazy val components = Helpers.stubControllerComponents(
    playBodyParsers = stubPlayBodyParsers(materializer)
)