You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为单个Scala Mockito测试用例启用宽松模式保留冗余桩?

问题场景

被测试代码

import concurrent.{ExecutionContext, Future}

trait Backend {
  def verifyUser(): Future[Unit]
  def accounts(): Future[String]
}

class Service(backend: Backend) {
  private implicit val executor: ExecutionContext = ExecutionContext.global
  def accounts(): Future[String] =
    (for (_ <- backend.verifyUser(); accs <- backend.accounts()) yield accs)
    .recover { case ex => s"failed: ${ex.getMessage}" }
}

测试代码

import org.scalatest._
import matchers.should
import flatspec.AsyncFlatSpec
import org.mockito.scalatest.AsyncIdiomaticMockito

class ServiceTest extends AsyncFlatSpec with AsyncIdiomaticMockito with should.Matchers {
  private trait Fixture {
    lazy val backend: Backend = mock[Backend]
    lazy val service: Service = new Service(backend)
  }

  it should "return the reason of a failure" in {
    val fixture = new Fixture {}
    import fixture._
    backend.verifyUser() returns Future.failed(new RuntimeException("verifyUser"))
    backend.accounts()   returns Future.failed(new RuntimeException("accounts"))
    service.accounts() map { result =>
      result should startWith ("failed: ")
    }
  }
}

报错信息

Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):

  1. -> at ServiceTest.$anonfun$new$1(StrictStubbingTest.scala:25)
    Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
    org.mockito.exceptions.misusing.UnnecessaryStubbingException:

由于verifyUser()执行失败后,accounts()方法不会被调用,导致对应的桩被Mockito判定为冗余,抛出上述异常。要求保留两个桩的同时让测试通过,且仅为当前测试用例启用宽松模式。


解决方案

有两种精准控制的实现方式:

方式一:为单个冗余桩标记宽松模式

直接对不会被触发的backend.accounts()桩使用lenient()包装,仅让这个桩跳过未调用校验,其他桩仍保持Mockito的严格校验规则:

it should "return the reason of a failure" in {
  val fixture = new Fixture {}
  import fixture._
  backend.verifyUser() returns Future.failed(new RuntimeException("verifyUser"))
  // 为该冗余桩启用宽松模式,忽略未调用检查
  lenient(backend.accounts()) returns Future.failed(new RuntimeException("accounts"))
  service.accounts() map { result =>
    result should startWith ("failed: ")
  }
}

方式二:为当前测试块启用全局宽松模式

如果需要让当前测试用例中的所有桩都跳过未调用校验,可以用lenient代码块包裹所有stub定义:

it should "return the reason of a failure" in {
  val fixture = new Fixture {}
  import fixture._
  // 该代码块内的所有桩都启用宽松模式
  lenient {
    backend.verifyUser() returns Future.failed(new RuntimeException("verifyUser"))
    backend.accounts() returns Future.failed(new RuntimeException("accounts"))
  }
  service.accounts() map { result =>
    result should startWith ("failed: ")
  }
}

两种方式都能满足需求,方式一更精准(仅针对冗余桩),方式二更适合需要批量设置的场景。


内容的提问来源于stack exchange,提问作者danissimo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:33:12