Scala:如何对我拥有的使用模拟/存根进行 API 调用的函数进行单元测试?

问题描述 投票:0回答:2

我有一个函数可以对外部 API 进行 API 调用

假设该函数执行如下简单操作。仅供参考,需要导入

scala.io.Source

  def myFunction(apiRequestUrl: String) : MyObject = {
    val response: String = Source.fromURL(apiRequestUrl).mkString
    val formatedResponse: MyObject = formatResponseFunction(response)
    formatedResponse
  }

我知道我可能收到的一些错误代码是 400、404 等...并且我想一般地处理由此产生的任何错误代码。这怎么可能做到呢?我发现的例子似乎是在测试一个人自己构建的 REST API,而不是对其他人外部 API 的函数调用

scala unit-testing mocking
2个回答
2
投票

为了模拟外部服务调用,您可以使用 Mockito,这是一个模拟框架。 Mockito 使用起来非常简单,您可以为外部调用提供存根。例如

val m = mock[io.Source.type]

在这里,您模拟源,然后在调用

fromUrl
函数时提供所需的行为。 即

when(m.fromUrl("external service url")) thenReturn("result")

1
投票

首先,你必须用模拟注入你想要替换的依赖项,函数内的静态调用不能被模拟/存根。

在您的场景中,您还有另一个问题:您的依赖项

Source
是一个对象,并且您不能模拟对象,只能模拟非最终类和特征。此外,模拟第 3 方 API 被认为是一种不好的做法。

解决所有这些问题的一个好方法是重写代码,例如

trait HttpAdapter {
  def fromUrl(apiRequestUrl: String): String = Source.fromURL(apiRequestUrl).mkString
}

object HttpAdapter extends HttpAdapter

def myFunction(apiRequestUrl: String, httpAdapter: HttpAdapter = HttpAdapter) : MyObject = {
  val response: String = httpAdapter.fromUrl(apiRequestUrl)
  val formatedResponse: MyObject = formatResponseFunction(response)
  formatedResponse
}

"myFunction" should "work" in {
  //create mock
  val http = mock[HttpAdapter]
  
  //stub mock
  http.fromUrl("some url") shouldReturn "result"
  
  //inject mock
  myFunction("some url", http) shouldBe MyObject
}

注意,我已将第三方 API 包装在一个我可以完全控制的类中 (

HttpAdapter
),然后我模拟该类

然后我注入

httpAdapter
作为参数,但我提供了一个默认值,因此调用者不需要担心它,而我仍然可以在测试代码中使用模拟或存根覆盖它。

另请注意,我使用了mockito-scala而不是常规的mockito,因此存根语法是不同的。

© www.soinside.com 2019 - 2024. All rights reserved.