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

ScalaTest:如何统计eventually块内方法的执行次数?

统计ScalaTest Eventually中getItem的调用次数

嘿,要统计Eventually.eventually里getItem(recordId)的调用次数,有几个简单好用的方案,你可以根据自己的测试场景来选:

方案1:用线程安全的原子计数器(通用场景)

如果不确定测试是否涉及多线程操作,用AtomicInteger来计数最稳妥,它能保证计数的准确性:

import java.util.concurrent.atomic.AtomicInteger
import org.scalatest.concurrent.Eventually
import org.scalatest.time.{Seconds, Span}

// 初始化计数器,初始值为0
val callCount = new AtomicInteger(0)

// 包装原getItem方法,每次调用时先自增计数器
def trackedGetItem(id: String) = {
  callCount.incrementAndGet() // 每次调用计数器+1
  getItem(id) // 调用原方法并返回结果
}

// 在Eventually块中使用包装后的方法
Eventually.eventually(
  timeout(Span(5, Seconds)), 
  interval(Span(1, Seconds))
) {
  trackedGetItem(recordId).record.forall(
    _.state.contains(Constants.RecordStatusComplete)
  ) shouldEqual true
}

// 最终获取总调用次数
println(s"getItem总共被调用了 ${callCount.get()} 次")

这个方案的优点是线程安全,不管Eventually内部是否有线程切换,计数都不会出错,适合绝大多数场景。

方案2:用普通可变变量(单线程场景)

如果你的测试是单线程执行的(ScalaTest的Eventually默认是单线程重试),用普通的var会更轻量,代码也更简洁:

import org.scalatest.concurrent.Eventually
import org.scalatest.time.{Seconds, Span}

var callCount = 0 // 初始化计数器

def trackedGetItem(id: String) = {
  callCount += 1 // 每次调用+1
  getItem(id)
}

// 执行Eventually逻辑
Eventually.eventually(
  timeout(Span(5, Seconds)), 
  interval(Span(1, Seconds))
) {
  trackedGetItem(recordId).record.forall(
    _.state.contains(Constants.RecordStatusComplete)
  ) shouldEqual true
}

// 获取调用次数
println(s"getItem调用次数:$callCount")

这个方案省去了原子类的开销,适合确定单线程的测试场景。

方案3:用Mockito验证(已使用Mockito的场景)

如果你的测试已经在使用Mockito做Mock,直接利用Mockito的调用验证功能就能统计次数,还能灵活预设返回值模拟重试过程:

import org.mockito.Mockito._
import org.scalatestplus.mockito.MockitoSugar
import org.scalatest.concurrent.Eventually
import org.scalatest.time.{Seconds, Span}

// 假设getItem是RecordService中的方法,先mock这个服务
val mockRecordService = mock[RecordService]

// 预设mock的返回值:模拟前两次返回未完成,第三次返回完成
when(mockRecordService.getItem(recordId))
  .thenReturn(Record(Seq(RecordItem(state = Some(Constants.RecordStatusPending)))))
  .thenReturn(Record(Seq(RecordItem(state = Some(Constants.RecordStatusPending)))))
  .thenReturn(Record(Seq(RecordItem(state = Some(Constants.RecordStatusComplete)))))

// 执行Eventually逻辑
Eventually.eventually(
  timeout(Span(5, Seconds)), 
  interval(Span(1, Seconds))
) {
  mockRecordService.getItem(recordId).record.forall(
    _.state.contains(Constants.RecordStatusComplete)
  ) shouldEqual true
}

// 验证并获取调用次数:这里会验证getItem被调用了3次
verify(mockRecordService, times(3)).getItem(recordId)

这个方案不需要额外写包装方法,Mockito会自动帮你记录调用次数,适合已经在使用Mockito的测试套件。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:57:33