如何在Mockito和Scala中使用隐式匹配器stub方法调用



我的应用程序代码使用AService

trait AService {
    def registerNewUser (username: String)(implicit tenant: Tenant): Future[Response]
}

注册一个新用户。类Tenant是一个简单的case类:

case class Tenant(val vstNumber:String, val divisionNumber:String) 

Trait AServiceMock通过使用AService的模拟版本来模拟注册逻辑

trait AServiceMock {
  def registrationService = {
    val service = mock[AService]
    service.registerNewUser(anyString) returns Future(fixedResponse)
    service
  }
}

每当在AService上调用registerNewUser时,响应将是"fixedResponse"(在其他地方定义)。

我的问题是,如何将隐式租户参数定义为像anyString这样的模拟匹配器?

顺便说一句。我使用Mockito与Specs2(和Play2)

有时候你必须先在SO上发帖,才能想出一个完全显而易见的答案:

service.registerNewUser(anyString)(any[Tenant]) returns Future(fixedResponse)

这是@simou答案的补充。现在我认为这就是它应该如何做,但我认为有趣的是知道为什么应该避免@Enrik提出的替代解决方案,因为它可能在运行时失败,在某些情况下出现一个神秘的错误。

你可以安全地做的是,如果你想在你的存根的隐式参数上精确匹配,你可以把它添加到作用域中:

trait AServiceMock {
  implicit val expectedTenant: Tenant = Tenant("some expected parameter")
  def registrationService = {
    val service = mock[AService]
    service.registerNewUser(anyString) returns Future(fixedResponse)
    service
  }
}

这将工作良好,但只有当服务。registerNewUser被调用的租户与隐式值expectedTenant提供的租户完全相同。

另一方面,

不能可靠地工作的是:

implicit val expectedTenant1: Tenant = any[Tenant]
implicit def expectedTenant2: Tenant = any[Tenant]
implicit def expectedTenant3: Tenant = eqTo(someTenant)

To reason与mockito如何创建参数匹配器有关。

当你写myFunction(*,12) returns "abc"时,mockito实际上使用一个宏:

  1. 添加代码来初始化参数匹配器可以注册的列表
  2. 如果需要,将所有不是matcher的参数包装在matchers中。
  3. 添加代码来检索为该函数声明的匹配器列表。

在expectedTenant2或expectedTenant3的情况下,可能会在对函数求值时注册第一个参数匹配器。但是宏不会看到这个函数正在注册一个母线程。它将只考虑该函数声明的返回类型,因此可能决定将该返回值包装在第二个匹配器中。

所以在实践中如果你有这样的代码
trait AServiceMock {
  implicit def expectedTenant(): Tenant = any[Tenant]
  def registrationService = {
    val service = mock[AService]
    service.registerNewUser(anyString) returns Future(fixedResponse)
    service
  }
}

您希望在应用隐式:

之后它是这样的:
trait AServiceMock {
 
  def registrationService = {
    val service = mock[AService]
    service.registerNewUser(anyString)(any[Tenant]) returns Future(fixedResponse)
    service
  }
}

但实际上mockito宏会使它或多或少像这样:

trait AServiceMock {
 
  def registrationService = {
    val service = mock[AService]
    // In practice the macro use DefaultMatcher and not eqTo but that do not change much for the matter we discuss.
    service.registerNewUser(anyString)(eqTo(any[Tenant])) returns Future(fixedResponse)
    service
  }
}

现在你在存根的隐式参数中声明了两个匹配器。当mockito将检索为registerNewUser声明的匹配器列表时,它将看到其中的三个,并将认为您试图为一个只需要两个参数的函数注册一个带有三个参数的存根,并记录:

Invalid use of argument matchers!
2 matchers expected, 3 recorded:

我还不确定为什么它在某些情况下仍然有效,我的假设是:

  • 也许宏有时决定在某些情况下不需要匹配器,并且不将隐式函数返回的值包装在额外的匹配器中。
  • 也许与一些宽大选项启用,模拟忽略额外的匹配器。即使是这种情况,额外的匹配器也可能会打乱存根的参数顺序。
  • 也有可能在某些情况下,scala编译器内联隐式def,这将允许宏看到使用了匹配器。

相关内容

  • 没有找到相关文章

最新更新