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

如何在Mockito Scala中Mock任意代码块?附MetricsLogger场景示例

在Mockito Scala中Mock代码块的解决方案

嘿,我来帮你搞定这个问题!你的MetricsLogger里的measure方法包裹了带有副作用的logMetric,想要在测试中控制这个副作用或者验证它的调用,对吧?下面分两种常见场景给你讲解具体实现:

场景1:保留原有measure逻辑,仅MocklogMetric方法

如果不想重构现有代码,我们可以用Mockito的spy来包装真实的MetricsLogger实例,这样既能保留measure的原有执行逻辑,又能替换掉logMetric的副作用行为。

第一步:调整logMetric的访问权限

首先要确保logMetric不是私有方法,这样Mockito才能拦截它。把它改成protected或者直接public:

import com.amazonaws.services.cloudwatch.model.StandardUnit

case class Metric(name: String, value: Long, unit: StandardUnit)

class MetricsLogger {
  def measure[T](name: String)(operation: => T): T = {
    val startTime = System.currentTimeMillis
    val result = try {
      operation
    } finally {
      logMetric(Metric(name, System.currentTimeMillis - startTime, StandardUnit.Milliseconds))
    }
    result
  }

  // 改为protected或public,允许测试时mock
  protected def logMetric(metric: Metric): Unit = {
    // 真实的CloudWatch上传逻辑
  }
}

第二步:编写测试代码

用Mockito Scala的spy和验证方法来实现测试:

import org.mockito.MockitoSugar
import org.scalatest.funsuite.AnyFunSuite
import com.amazonaws.services.cloudwatch.model.StandardUnit

class MetricsLoggerTest extends AnyFunSuite with MockitoSugar {
  test("measure方法应正确执行操作并调用logMetric") {
    // 创建spy实例:保留原有measure逻辑,仅mock logMetric
    val loggerSpy = spy(new MetricsLogger)

    // 模拟你的网络操作
    val testOperationResult = "api_response"

    // 调用measure方法
    val result = loggerSpy.measure("network_call_metric") {
      testOperationResult
    }

    // 验证操作结果是否正确返回
    assert(result == testOperationResult)

    // 验证logMetric是否被调用,且参数符合预期(忽略具体耗时,只检查名称和单位)
    verify(loggerSpy).logMetric(argThat { metric: Metric =>
      metric.name == "network_call_metric" && 
      metric.unit == StandardUnit.Milliseconds &&
      metric.value >= 0
    })

    // 如果只想完全抑制logMetric的副作用,不需要验证的话,可以用这句:
    // doNothing.when(loggerSpy).logMetric(any[Metric])
  }
}

场景2:通过依赖注入解耦,更优雅地Mock副作用

如果可以重构代码,把logMetric的逻辑抽离成独立的依赖(比如一个MetricClient接口),测试时直接Mock这个依赖会更简洁,也符合面向对象的设计原则。

第一步:重构代码,引入依赖

trait MetricClient {
  def logMetric(metric: Metric): Unit
}

// 真实的CloudWatch实现
class CloudWatchMetricClient extends MetricClient {
  override def logMetric(metric: Metric): Unit = {
    // 上传指标到CloudWatch的逻辑
  }
}

class MetricsLogger(metricClient: MetricClient) {
  def measure[T](name: String)(operation: => T): T = {
    val startTime = System.currentTimeMillis
    val result = try {
      operation
    } finally {
      metricClient.logMetric(Metric(name, System.currentTimeMillis - startTime, StandardUnit.Milliseconds))
    }
    result
  }
}

第二步:编写测试代码

现在可以直接MockMetricClient,不需要用spy:

import org.mockito.MockitoSugar
import org.scalatest.funsuite.AnyFunSuite
import com.amazonaws.services.cloudwatch.model.StandardUnit

class MetricsLoggerTest extends AnyFunSuite with MockitoSugar {
  test("measure方法执行后应委托给MetricClient记录指标") {
    // 创建Mock的MetricClient
    val mockClient = mock[MetricClient]
    val logger = new MetricsLogger(mockClient)

    // 模拟网络操作
    val testResult = "success"

    // 调用measure
    val result = logger.measure("api_request") { testResult }

    // 验证结果返回正确
    assert(result == testResult)

    // 验证MetricClient的logMetric方法被正确调用
    verify(mockClient).logMetric(argThat(metric => metric.name == "api_request"))
  }
}

关键要点总结

  • Spy vs Mock:如果要保留原有类的部分方法逻辑,用spy;如果要完全替换依赖,用mock
  • 要Mock的方法不能是私有方法,否则Mockito无法拦截
  • 依赖注入是更优雅的测试方案,能让代码的职责更清晰,也更易测试

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 07:05:20