如何在Scala中实现支持多场景的重载verify验证方法?
如何在Scala中实现支持多场景的重载verify验证方法?
我完全懂你想要的效果:能灵活调用verify,不管是带不带验证模式参数,也不管是在常规验证还是inOrder顺序验证场景里,还不想写那个看起来有点别扭的implicit order =>代码。先聊聊你遇到的核心问题:一是重载方法不能同时带默认参数,直接卡了最初的思路;二是类级别加隐式默认值会导致歧义,除非参数名字一致,但要求用户按指定名字写变量确实不够优雅。
下面给你两个更顺畅的解决方案:
方案一:利用隐式参数的“存在性”区分重载
我们可以定义两组verify方法:一组对应常规验证(无InOrder上下文),另一组对应顺序验证(依赖隐式InOrder实例),同时分别处理带/不带验证模式的情况:
import org.mockito.{Mockito, InOrder, VerificationMode} import org.mockito.Mockito.times // 常规场景的verify(无InOrder上下文时调用) def verify[T](mock: T): T = Mockito.verify(mock) def verify[T](mock: T, mode: VerificationMode): T = Mockito.verify(mock, mode) // 顺序验证场景的verify(仅当作用域存在隐式InOrder时生效) def verify[T](mock: T)(implicit inOrder: InOrder): T = inOrder.verify(mock) def verify[T](mock: T, mode: VerificationMode)(implicit inOrder: InOrder): T = inOrder.verify(mock, mode) // 重写inOrder方法,自动把InOrder设为块内隐式参数 def inOrder(mocks: AnyRef*)(block: => Unit): Unit = { implicit val order: InOrder = Mockito.inOrder(mocks: _*) block }
这样用起来完全符合你的预期,还不用写多余的隐式参数声明:
class SomeTest { // 常规验证调用 verify(foo).bar(any) verify(foo, times(2)).baz(any) // 顺序验证调用,代码清爽很多 inOrder(foo) { verify(foo).bar(any) verify(foo, times(2)).baz(any) } }
这个方案的核心是Scala的隐式解析规则:块内存在隐式InOrder时,会自动选择顺序验证版的verify;块外没有则用常规Mockito验证,完全不需要用户手动区分。
方案二:用包装类统一管理验证上下文
如果觉得四个重载方法有点冗余,可以用一个包装类来封装不同的验证逻辑,扩展性更强:
import org.mockito.{Mockito, InOrder, VerificationMode} import org.mockito.Mockito.times // 定义验证上下文的公共接口 sealed trait VerifyContext { def verify[T](mock: T): T def verify[T](mock: T, mode: VerificationMode): T } // 常规验证上下文实现 object DefaultVerifyContext extends VerifyContext { override def verify[T](mock: T): T = Mockito.verify(mock) override def verify[T](mock: T, mode: VerificationMode): T = Mockito.verify(mock, mode) } // 顺序验证上下文实现 class InOrderVerifyContext(inOrder: InOrder) extends VerifyContext { override def verify[T](mock: T): T = inOrder.verify(mock) override def verify[T](mock: T, mode: VerificationMode): T = inOrder.verify(mock, mode) } // 全局隐式默认上下文(常规场景自动使用) implicit val defaultVerifyContext: VerifyContext = DefaultVerifyContext // 对外暴露的统一verify方法,依赖隐式上下文 def verify[T](mock: T)(implicit ctx: VerifyContext): T = ctx.verify(mock) def verify[T](mock: T, mode: VerificationMode)(implicit ctx: VerifyContext): T = ctx.verify(mock, mode) // inOrder方法替换块内的隐式上下文 def inOrder(mocks: AnyRef*)(block: => Unit): Unit = { implicit val ctx: VerifyContext = new InOrderVerifyContext(Mockito.inOrder(mocks: _*)) block }
用法和方案一完全一致,但后续如果要新增其他验证规则(比如超时验证、自定义校验逻辑),只需要新增VerifyContext的实现即可,维护起来更方便。
关于你之前尝试的补充说明
你提到的“把类成员变量名和块参数名设为一致就能解决歧义”,本质是Scala的隐式解析优先级:局部作用域的隐式变量会优先于类级别的,但这种方式对用户不友好,很容易因为写错名字出问题,所以上面的方案都避开了这种限制。
备注:内容来源于stack exchange,提问作者Dima
相关产品推荐
相关产品推荐

