Как я могу протестировать приложение Play, которое использует WsClient для выполнения запросов? - PullRequest
0 голосов
/ 31 января 2020

У меня есть приложение Play 2.7.x, которое должно переслать запрос на другой URL. В нем реализован контроллер, подобный следующему:

import play.api.mvc._
import play.api.libs.ws.WSClient

class MyController(val controllerComponents: ControllerComponents, ws: WSClient, baseUrl: String) extends BaseController {
  // POST /foo routed here
  def foo() = {
    Action.async { request =>
        ws.url(s"http://$baseUrl/foo").post(equest.body.asJson.get).map(_ -> Ok(""))
    }
  }
}

Теперь я хочу создать тест, который делает поддельный запрос и использует поддельный сервер для проверки всего. После https://www.playframework.com/documentation/2.7.x/ScalaTestingWebServiceClients я попробовал следующее:

class ControllersSpec extends PlaySpec with OneAppPerSuiteWithComponents {

  override lazy val components = new BuiltInComponentsFromContext(context) with NoHttpFiltersComponents with AhcWSComponents {
    lazy val myController = new MyController(controllerComponents, wsClient, "localhost:19001")

    lazy val router = new Routes(httpErrorHandler, myController)
  }

  "My Controller" should {
    "forward requests" in {
      Server.withRouterFromComponents(ServerConfig(port=Some(19001))) {
        import play.api.routing.sird._
        routes: BuiltInComponents => {
          case POST(p"/foo") =>
            routes.defaultActionBuilder {
              Ok("")
            }
        }
      } { port =>
        route(app, FakeRequest(POST, "/foo", FakeHeaders(), "{\"foo\": \"bar\"}")  // <-- line 86
      }
    }
  }
}

Однако, пытаясь запустить это приводит к:

java.lang.IllegalStateException: cannot create children while terminating or terminated
    at akka.actor.dungeon.Children$class.makeChild(Children.scala:270)
    at akka.actor.dungeon.Children$class.attachChild(Children.scala:48)
    at akka.actor.ActorCell.attachChild(ActorCell.scala:370)
    at akka.stream.impl.ExtendedActorMaterializer.actorOf(ActorMaterializerImpl.scala:60)
    at akka.stream.impl.GraphStageIsland.onIslandReady(PhasedFusingActorMaterializer.scala:742)
    at akka.stream.impl.PhasedFusingActorMaterializer.materialize(PhasedFusingActorMaterializer.scala:507)
    at akka.stream.impl.PhasedFusingActorMaterializer.materialize(PhasedFusingActorMaterializer.scala:420)
    at akka.stream.impl.PhasedFusingActorMaterializer.materialize(PhasedFusingActorMaterializer.scala:415)
    at akka.stream.scaladsl.RunnableGraph.run(Flow.scala:496)
    at akka.stream.scaladsl.Source.runWith(Source.scala:83)
    at play.api.libs.streams.StrictAccumulator.run(Accumulator.scala:203)
    at play.api.test.EssentialActionCaller$class.call(Helpers.scala:249)
    at play.api.test.Helpers$.call(Helpers.scala:601)
    at play.api.test.RouteInvokers$class.route(Helpers.scala:271)
    at play.api.test.Helpers$.route(Helpers.scala:601)
    at play.api.test.RouteInvokers$class.route(Helpers.scala:281)
    at play.api.test.Helpers$.route(Helpers.scala:601)
    at controllers.ControllersSpec$$anonfun$1$$anonfun$apply$mcV$sp$2$$anonfun$3.apply(ControllersSpec.scala:86)

Я предполагаю, что тестовый сервер и использование приложением Akka мешает, и одно преждевременно завершает работу другого, но я недостаточно хорошо знаю их работу, чтобы понять это полностью. Как правильно настроить такой тест?

1 Ответ

1 голос
/ 04 февраля 2020

Если вы не возражаете против использования внешней библиотеки, я бы предложил две из них:

Play MockWS :

Это, вероятно, то, что вы хотите, чтобы настроить его нужно просто:

val mockWs = MockWS {
  case ("POST", "/foo") => Action { Ok(Json.parse("{\"foo\": \"bar\"}")) }
}

override lazy val app: Application = new GuiceApplicationBuilder()
   //and override guice WSClient instance with MockWS
  .overrides(bind[WSClient].toInstance(mockWs))
  .build()

Siremock :

Siremock - это обертка Wiremock. Его сложнее настроить, но у него есть много других функций, кроме mock, например, проверка того, что ваш запрос действительно выполнен:

val request = 
  (on(urlEqualTo("/foo")))
    .withHttpMethod(HttpMethods.POST)
    .withContentType("application/json")

request respond (aResponse withBody "{\"foo\": \"bar\"}")

//execute your test here

verify(request) wasCalled exactly(1)

[] s

...