这是对@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
}
}
这样做是可行的,但前提是 service.registerNewUser 函数需要接收与期望的租户(由隐式值 expectedTenant 提供)完全相同的租户参数。
另一方面,以下风格的任何内容都不会可靠地起作用:
implicit val expectedTenant1: Tenant = any[Tenant]
implicit def expectedTenant2: Tenant = any[Tenant]
implicit def expectedTenant3: Tenant = eqTo(someTenant)
理性与Mockito如何创建其参数匹配器有关。
当您编写
myFunction(*,12)returns“abc”
时,Mockito实际上使用了一个宏:
- 添加代码以初始化列表,其中参数匹配器可以注册
- 如果需要,将所有不是匹配器的参数包装在匹配器中。
- 添加代码以检索为此函数声明的匹配器列表。
在预期的expectedTenant2或expectedTenant3的情况下,可能会在评估该函数时注册第一个参数匹配器。但是,该宏将不会看到此函数正在注册macther。它只会考虑此函数声明的返回类型,因此可能决定将返回的值包装在第二个匹配器中。
因此,在实践中,如果您有以下代码:
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]
service.registerNewUser(anyString)(eqTo(any[Tenant])) returns Future(fixedResponse)
service
}
}
现在你在stub的隐式参数中声明了两个matcher。当Mockito检索为registerNewUser声明的匹配器列表时,它将看到其中三个,并认为你正在尝试为一个只需要两个参数的函数注册具有三个参数的存根,并记录:
Invalid use of argument matchers!
2 matchers expected, 3 recorded:
我还不确定为什么它在某些情况下仍然有效,我的假说是:
- 也许宏有时会在某些情况下决定不需要匹配器,并且不会在隐式函数返回的值周围包装额外的匹配器。
- 也许启用了某些宽容选项,mockito会忽略额外的匹配器。即使是这种情况,额外的匹配器也可能会搅乱你存根的参数顺序。
- 还可能在某些情况下,scala编译器内联隐式定义,这将允许宏看到使用了匹配器的情况。
implicit def tenantMatcher = any[Tenant]
怎么样? - Eric