Scala:如何对我拥有的使用mock/stub进行API调用的函数进行单元测试?

Scala:如何对我拥有的使用mock/stub进行API调用的函数进行单元测试?,scala,unit-testing,mocking,Scala,Unit Testing,Mocking,我有一个函数,可以调用外部API 让我们假设这个函数做了如下简单的事情。仅供参考,我需要导入scala.io.Source def myFunction(apiRequestUrl: String) : MyObject = { val response: String = Source.fromURL(apiRequestUrl).mkString val formatedResponse: MyObject = formatResponseFunction(respons

我有一个函数,可以调用外部API

让我们假设这个函数做了如下简单的事情。仅供参考,我需要导入
scala.io.Source

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

我知道我可能收到的一些错误代码是400、404等等。。。我只想从这里一般地处理任何错误代码。这怎么可能呢?我发现的示例似乎是测试一个人自己构建的RESTAPI,而不是对其他人的外部API的函数调用。要模拟外部服务调用,可以使用Mockito,这是一个模拟框架。Mockito使用非常简单,您可以为外部调用提供存根。比如说

val m = mock[io.Source.type]
这里模拟源代码,然后在调用
fromUrl
函数时提供所需的行为。 i、 e


首先,必须注入要替换为mock的依赖项,函数中的静态调用不能被mock/stubbed

在您的场景中,您还有另一个问题:您的依赖项
Source
是一个对象,您不能模拟对象,只能模拟非最终类和特征。此外,模拟第三方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,因此存根语法是不同的

谢谢您的回复。我还是新手,所以也感谢您分享一些最佳实践。这个特性示例会被称为适配器模式吗?我注意到您编写了“object HttpAdapter extends”,但没有说明它扩展了什么。你能完成吗?很高兴能帮上忙哦,是的,复制粘贴错误,我现在修复了扩展-如果它解决了您的问题,请将其标记为正确答案,这样可以使更多人受益!是的,这太棒了。谢谢。在使用常规Mockito框架尝试此操作后,我收到一个错误,指出“get$default$2()get$default$2()无法返回字符串,应该返回int”。常规Mockito与某些scala功能不兼容,该错误似乎与返回值类的默认参数有关(这里只是一个猜测)。。。你能试试mockito scala吗?如果它在那里不起作用,请在github中创建一个问题,并提供足够的代码,以便可以轻松地复制和粘贴它以重现错误
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
}