Salahzar Stenvaag
Salahzar Stenvaag

Reputation: 23

ScalaPlay > 2.6 how to access POST requests while faking a trivial server in tests

I'm trying to setup a fake server with Play2.7 and the environment suggested by https://developer.lightbend.com/guides/play-rest-api/ just echoing json from a POST request. While I was able to make GET and POST requests returning hardwired values I can't access directly the request to return or process it. NOTE: this was doable with versions < 2.6 but now Action has become deprecated, so I'm wondering which is the correct way to deal with this in Play >= 2.6

I have read the following how to mock external WS API calls in Scala Play framework and How to unit test servers in Play 2.6 now that Action singleton is deprecated which are actually doing almost all I am trying to do, but it seems I need something different to access the Request. In previous version of Play I could do something like the following:

case POST(p"/route") => Action { request => Ok(request.body.asJson.getOrElse(JsObject.empty)) }

But it seems calling the action this way is not more possible since I received the 'infamous'

object Action in package mvc is deprecated: Inject an ActionBuilder (e.g. DefaultActionBuilder) or extend BaseController/AbstractController/InjectedController

error.

my actual working code is

object FakeServer {

  def withServerForStep1[T](codeBlock: WSClient => T): T =
    Server.withRouterFromComponents() { cs =>
      {
        case POST(p"/route") =>
          cs.defaultActionBuilder {
            Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
          }
      }
    } { implicit port =>
      WsTestClient.withClient(codeBlock)
    }
}

and the unit Spec is something like

"The step 1" should {
    "Just call the fakeservice" in {
      setupContext()
      FakeServer.withServerForStep1 ( {
        ws =>
          val request = ws.url("/route")
          val data = Json.obj(
            "key1" -> "value1",
            "key2" -> "value2"
          )
          val response = request.post(data).futureValue

          response.status mustBe 200
          response.body mustBe Json.toJson(data)

      })
    }
  }

I would like to write the FakeServer in such a way that the Spec will succeed in checking that returned body is equal to original sent json. Currently it is obviously failing with

"[{"full_name":"octocat/Hello-World"}]" was not equal to {"key1":"value1","key2":"value2"}

Upvotes: 1

Views: 350

Answers (1)

Salahzar Stenvaag
Salahzar Stenvaag

Reputation: 23

I eventually found how to do it, and the correct way as often happens in Scala is... trivial.

The "trick" was just to add request => in the body of cs.defaultActionBuilder as in the next example

 object FakeServer {

  def withServerForStep1[T](codeBlock: WSClient => T): T =
    Server.withRouterFromComponents() { cs =>
      {
        case POST(p"/route") =>

          cs.defaultActionBuilder { request =>
            val bodyAsJson = request.body.asJson.getOrElse(JsObject.empty)
            Results.Ok(bodyAsJson)
          }
      }
    } { implicit port =>
      WsTestClient.withClient(codeBlock)
    }
}

Then the test just needed to deal with possible extra wrapping quotes and reads as

 val response = request.post(data).futureValue

        response.status mustBe 200
        response.body mustBe Json.toJson(data).toString()

Upvotes: 1

Related Questions