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

如何让Rspec的class_double每次调用返回不同REST响应

解决Rspec中多次REST调用返回不同模拟响应的问题

我需要用Rspec测试Device类,其中涉及对Request类和HTTPI模块的模拟,返回REST响应。当前单次调用能正常工作,但当方法发起第二次REST调用时,需要返回新的响应,这个需求还没实现。

相关代码

API类(简化版本,核心逻辑完整)

class API
  include HTTPI
  
  def find_device(id)
    req = create_request('path')
    req.body = { :customerId => MultiJson.dump(customer_id) }
    return call(req)
  end
  
  def find_other_device(other_id)
    req = create_request('path')
    req.body = { :other_id => MultiJson.dump(other_id) }
    data = call(req)
    return data
  end
  
  def call(req)
    response = HTTPI.send(req)
    return response.body
  end
end

Device类(调用REST方法)

class Device
  @api = API.new(:open_timeout => 30, :read_timeout => 30)
  def get_device
    devices = @api.find_device(@id)
    log.info("First Call Made")
    other_call = @api.find_other_device(@other_id)
  end
end

当前测试文件

Rspec.describe Device do
  resp = {code: 200, body: resp_body, raw_body: "TEST BODY", cookies: [cookie_list_hash]}
  resp2 = {code: 200, body: resp_body_2, raw_body: "TEST BODY 2", cookies: [cookie_list_hash]}
  let!(:request) {class_double('Request', new: http).as_stubbed_const} # 我知道这会导致HTTPI.send始终返回相同的resp,但没有它测试甚至无法完成第一次调用
  let!(:http) {class_double('HTTPI', send: resp).as_stubbed_const}

  it 'test' do
    Device.get_device
  end
end

期望效果:让模拟的HTTPI.send第一次调用返回resp,第二次返回resp2。

解决方案

要实现多次调用返回不同值,你需要用Rspec的and_return方法指定调用序列的返回结果,同时调整HTTPI的double设置:

修改测试文件中的double定义,把固定返回resp改成按顺序返回resp和resp2:

Rspec.describe Device do
  let(:resp) { {code: 200, body: resp_body, raw_body: "TEST BODY", cookies: [cookie_list_hash]} }
  let(:resp2) { {code: 200, body: resp_body_2, raw_body: "TEST BODY 2", cookies: [cookie_list_hash]} }
  
  # 模拟Request类
  let!(:request_double) { class_double('Request').as_stubbed_const }
  # 模拟HTTPI模块
  let!(:httpi_double) { class_double('HTTPI').as_stubbed_const }

  it 'returns different responses for multiple API calls' do
    # 指定HTTPI.send按调用顺序返回不同响应
    expect(httpi_double).to receive(:send)
      .and_return(resp, resp2)
    
    # 注意:如果get_device是实例方法,需要先创建Device实例
    device = Device.new
    device.get_device
  end
end

关键说明

  • expect(httpi_double).to receive(:send).and_return(resp, resp2)可以让HTTPI.send按调用顺序依次返回指定响应。
  • 无需将Request的new方法绑定到HTTPI的double,测试重点是HTTPI的返回结果,只要Request类被正确模拟占位即可。
  • 将resp和resp2改为let定义,确保它们在测试上下文里正确初始化,避免作用域问题。

另外,你的Device类中get_device是实例方法,调用时必须先创建Device实例;如果要作为类方法调用,需要修改Device类:

class Device
  @api = API.new(:open_timeout => 30, :read_timeout => 30)
  def self.get_device
    devices = @api.find_device(@id)
    log.info("First Call Made")
    other_call = @api.find_other_device(@other_id)
  end
end

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 12:37:30