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

如何用Minitest测试外部API轮询?Mock/Stub请求失败求助

解决Mocha Mock/Stub BaseApi 相关调用的问题

看起来你在测试Sidekiq Worker时,Mock BaseApi.authenticate 和 BaseApi::TrackableJob 这两个API调用遇到了问题。结合你提供的代码和Mocha的特性,我来帮你梳理下正确的Mock方式:

核心问题分析

你的代码里有两个关键依赖需要Mock:

  1. BaseApi.authenticate(1) 是一个带块的方法,必须让它执行块内的逻辑,否则后续的TrackableJob调用不会触发;
  2. BaseApi::TrackableJob.find(job_id).first 是链式调用,需要分步Mock才能覆盖到.first的返回值。

具体测试用例示例

下面是针对不同Job状态的测试代码,你可以直接参考:

describe BaseApis::PollTrackableJobWorker do
  let(:job_id) { 'test-job-uuid' }
  let(:dummy_invoice_id) { 'dummy-invoice-123' }

  # 测试状态为'done'的场景
  it 'returns true when the job completes successfully' do
    # Mock BaseApi.authenticate,指定参数并让它执行块内代码
    BaseApi.expects(:authenticate).with(1).yields

    # 链式Mock TrackableJob.find -> first
    trackable_job_mock = mock('TrackableJobResponse')
    trackable_job_mock.expects(:first).returns({ 'status' => 'done' })
    BaseApi::TrackableJob.expects(:find).with(job_id).returns(trackable_job_mock)

    worker = BaseApis::PollTrackableJobWorker.new
    result = worker.perform(job_id, dummy_invoice_id)

    expect(result).to be(true)
  end

  # 测试状态为'error'的场景
  it 'triggers Bugsnag notification when job fails' do
    error_details = { 'status' => 'error', 'errors' => 'Failed to process document' }

    BaseApi.expects(:authenticate).with(1).yields
    trackable_job_mock = mock('TrackableJobResponse')
    trackable_job_mock.expects(:first).returns(error_details)
    BaseApi::TrackableJob.expects(:find).with(job_id).returns(trackable_job_mock)

    # 验证Bugsnag.notify被正确调用
    Bugsnag.expects(:notify).with('Failed to process document')

    worker = BaseApis::PollTrackableJobWorker.new
    worker.perform(job_id, dummy_invoice_id)
  end

  # 测试状态为'pending'或'running'的场景
  it 'raises EDocumentNotSentError for pending/running jobs' do
    ['pending', 'running'].each do |status|
      BaseApi.expects(:authenticate).with(1).yields
      trackable_job_mock = mock('TrackableJobResponse')
      trackable_job_mock.expects(:first).returns({ 'status' => status })
      BaseApi::TrackableJob.expects(:find).with(job_id).returns(trackable_job_mock)

      worker = BaseApis::PollTrackableJobWorker.new
      expect { worker.perform(job_id, dummy_invoice_id) }.to raise_error(EDocumentNotSentError)
    end
  end
end

关键注意事项

  • 必须给authenticate加.yields:如果忘记这个,块内的代码不会执行,你Mock的TrackableJob调用永远不会触发,看起来就像Mock失败了;
  • 链式调用要分步Mock:不能直接写BaseApi::TrackableJob.expects(:find).with(job_id).first.returns(...),得先让find返回一个mock对象,再给这个对象的first方法设置返回值;
  • 参数匹配要准确:确保authenticate的参数1和find的job_id都和代码里的调用完全匹配,否则Mocha会认为调用不匹配,Mock不生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:10:18