如何在Scala Play框架中模拟外部WS API调用

时间:2017-07-11 03:54:06

标签: scala rest unit-testing playframework mocking

我有一个现有的Scala play应用程序,它有一个调用另一个外部REST API的REST API。我想模拟外部Web服务返回虚假的JSON数据进行内部测试。基于以下示例:astype

我完全按照文档中的说明操作,由于不推荐使用类操作,我收到编译器错误。

import play.core.server.Server
import play.api.routing.sird._
import play.api.mvc._
import play.api.libs.json._
import play.api.test._

import scala.concurrent.Await
import scala.concurrent.duration._

import org.specs2.mutable.Specification
import product.services.market.common.GitHubClient

class GitHubClientSpec extends Specification {
  import scala.concurrent.ExecutionContext.Implicits.global

  "GitHubClient" should {
    "get all repositories" in {

      Server.withRouter() {
        case GET(p"/repositories") => Action {
          Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
        }
      } { implicit port =>
        WsTestClient.withClient { client =>
          val result = Await.result(
            new GitHubClient(client, "").repositories(), 10.seconds)
          result must_== Seq("octocat/Hello-World")
        }
      }
    }
  }
}
  

对象不推荐使用包mvc中的对象:注入ActionBuilder   (例如DefaultActionBuilder)或扩展   BaseController /一个AbstractController / InjectedController

这是最新官方文档的主要示例,其实际上包含编译时错误,鉴于此示例不起作用,应该如何使用Scala Play轻松模拟外部API?

2 个答案:

答案 0 :(得分:1)

您可以将示例更改为:

Server.withRouterFromComponents() { cs => {
    case GET(p"/repositories") => cs.defaultActionBuilder {
      Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
    }
  }
} { implicit port =>
  WsTestClient.withClient { client =>
    val result = Await.result(
      new GitHubClient(client, "").repositories(), 10.seconds)
    result should be(Seq("octocat/Hello-World"))
  }
}

说实话,我不能100%确定这是不是最好的方式。但是我已经向游戏框架提交了PR,因此您可能会看到这些空间以供制作者发表评论。

答案 1 :(得分:0)

如果你使用的是独立版本的play-ws,你可以像这样使用这个库https://github.com/f100ded/play-fake-ws-standalone

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import org.f100ded.play.fakews._
import org.scalatest._
import play.api.libs.ws.JsonBodyWritables._

import scala.concurrent.duration.Duration
import scala.concurrent._
import scala.language.reflectiveCalls

/**
  * Tests MyApi HTTP client implementation
  */
class MyApiClientSpec extends AsyncFlatSpec with BeforeAndAfterAll with Matchers {

  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()
  import system.dispatcher

  behavior of "MyApiClient"

  it should "put access token to Authorization header" in {
    val accessToken = "fake_access_token"
    val ws = StandaloneFakeWSClient {
      case request @ GET(url"http://host/v1/foo/$id") =>
        // this is here just to demonstrate how you can use URL extractor
        id shouldBe "1"

        // verify access token
        request.headers should contain ("Authorization" -> Seq(s"Bearer $accessToken"))

        Ok(FakeAnswers.foo)
    }

    val api = new MyApiClient(ws, baseUrl = "http://host/", accessToken = accessToken)
    api.getFoo(1).map(_ => succeed)
  }

  // ... more tests

  override def afterAll(): Unit = {
    Await.result(system.terminate(), Duration.Inf)
  }

}