模拟BlazeClientBuilder [IO]以返回模拟客户端[IO]

时间:2019-02-08 07:58:04

标签: scala mockito scala-cats io-monad

我正在使用BlazeClientBuilder[IO].resource方法来获取Client[IO]。现在,我想模拟客户端以进行单元测试,但无法弄清楚该如何做。有什么好方法可以模拟这个,我该怎么做?

class ExternalCall(val resource: Resource[IO, Client[IO]], externalServiceUrl: Uri) {
def retrieveData: IO[Either[Throwable, String]] = {
for {
  req <- IO(Request[IO](Method.GET, uri = externalServiceUrl))
  response <- resource.use(client => {
    client.fetch[String](req)(httpResponse => {
      if (!httpResponse.status.isSuccess)
        throw new Exception(httpResponse.status.reason)
      else
        httpResponse.as[String]
    })
  })
} yield Right(response)
}
}

呼叫者代码

new ExternalCall(BlazeClientBuilder[IO](global).resource).retrieveData

2 个答案:

答案 0 :(得分:1)

看来您只需要做类似的事情

val resourceMock = mock[Resource[IO, Client[IO]]]
//stub whatever is necessary
val call = new ExternalCall(resourceMock).retrieveData
//do asserts and verifications as needed

编辑:

您可以在下面看到一个完整的示例,但我想强调一下,这是一个很好的例子,说明为什么避免不拥有的模拟API是一种很好的做法。

一种更好的测试方法是将与http4s相关的代码放入您拥有的类(YourHttpClient或其他任何类)中,并为该类编写一个集成测试,以检查http4s客户端是否做对了事情(您可以使用Wiremock模拟真实的http服务器)。

然后,您可以将YourHttpClient的模拟传递给依赖于它的组件,其优点是可以控制它的API,因此它将更加简单,并且如果http4s更新了它的API,则您只有一个中断类,而不是必须修复数十或数百个模拟交互。

顺便说一句,该示例是使用嘲笑标量编写的,因为使用嘲笑的Java版本会产生难以阅读的代码。

    val resourceMock = mock[Resource[IO, Client[IO]]]
    val clientMock   = mock[Client[IO]]
    val response: Response[IO] = Response(Status.Ok,
                                          body = Stream("Mocked!!!").through(text.utf8Encode),
                                          headers = Headers(`Content-Type`(MediaType.text.plain, Charset.`UTF-8`)))

    clientMock.fetch[String](any[Request[IO]])(*) shouldAnswer { (_: Request[IO], f: Response[IO] => IO[String]) =>
      f(response)
    }

    resourceMock.use[String](*)(*) shouldAnswer { (f: Client[IO] => IO[String]) =>
      f(clientMock)
    }

    val data = new ExternalCall(resourceMock, Uri.unsafeFromString("http://www.example.com")).retrieveData

    data.unsafeRunSync().right.value shouldBe "Mocked!!!"

答案 1 :(得分:0)

您可以使用以下代码段轻松模拟客户端

import fs2.Stream
import org.http4s.Response
import org.http4s.client.Client

def httpClient(body: String): Client[IO] = Client.apply[IO] { _ =>
    Resource.liftF(IO(Response[IO](body = Stream.emits(body.getBytes("UTF-8")))))
}

为了将客户端作为资源,您需要用 IO 包装它并提升到 Resource

Resource.liftF(IO(httpClient("body")))