将 BlazeClientBuilder[IO] 模拟为 return 模拟客户端 [IO]
Mocking of BlazeClientBuilder[IO] to return mock client[IO]
我正在使用 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
看来你只需要做类似的事情
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 相关的代码放在您拥有的 class 中(YourHttpClient
或其他),并为该 class 编写集成测试检查 http4s 客户端是否正确执行操作(您可以使用 wiremock 来模拟真实的 http 服务器)。
然后你可以将 YourHttpClient
的模拟传递给依赖它的组件,优点是你可以控制它的 API 所以它会更简单,如果 http4s 更新它的 API 你只有一个破坏 class 而不是必须修复数十或数百个模拟交互。
顺便说一句,该示例是使用 mockito-scala 编写的,因为使用 Java 版本的 mockito 会产生更难阅读的代码。
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!!!"
您可以使用以下代码片段轻松模拟客户端
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")))
我正在使用 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
看来你只需要做类似的事情
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 相关的代码放在您拥有的 class 中(YourHttpClient
或其他),并为该 class 编写集成测试检查 http4s 客户端是否正确执行操作(您可以使用 wiremock 来模拟真实的 http 服务器)。
然后你可以将 YourHttpClient
的模拟传递给依赖它的组件,优点是你可以控制它的 API 所以它会更简单,如果 http4s 更新它的 API 你只有一个破坏 class 而不是必须修复数十或数百个模拟交互。
顺便说一句,该示例是使用 mockito-scala 编写的,因为使用 Java 版本的 mockito 会产生更难阅读的代码。
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!!!"
您可以使用以下代码片段轻松模拟客户端
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")))