如何用RSpec同时验证Ruby方法调用的JSON参数与多参数?
问题:RSpec中如何验证方法的多个参数?
我正在为一个Ruby类编写RSpec测试,该类会将给定的ActiveRecord对象转换为JSON后,传递给KinesisHelper类的send_record_to_kinesis方法。
业务代码如下:
include KinesisHelper class OrderProcesser class << self def post_order(order) json = extract_order_params(order) KinesisHelper.send_record_to_kinesis(json, order.stream) end private def extract_order_params(order) params = {} params[:id] = order.id params[:max_budget] = order.budget params[:startDate] = order.start_date.to_i * 1000 params[:endDate] = order.end_date.to_i * 1000 params.to_json end end end
在RSpec测试中,我希望验证被测类生成的JSON格式正确,且调用send_record_to_kinesis时传入的两个参数均符合预期。我的测试尝试代码如下:
require 'spec_helper' describe OrderProcesser do describe "#perform" do before do @order = FactoryBot.create(:order) end it "should invoke OrderProcesser" do # 可用的通用验证 #KinesisHelper.should_receive(:send_record_to_kinesis).once.with(anything, @order.stream) # 仅第一个条件生效的验证尝试 expect(KinesisHelper).to receive(:send_record_to_kinesis) { |arg1, arg2| expect(arg2).to eq @order.stream expect(arg1[:id]).to eq @order.id # 未被校验 } #执行方法 OrderProcesser.post_order(@order) end end end
通用参数验证可以正常通过,但当我尝试同时验证多个参数(如JSON内容和第二个参数值)时,只有第一个条件会被校验,第二个条件似乎被忽略。请问如何在RSpec中对方法的多个参数执行完整的多匹配校验?
解决方法
问题根源
你遇到的问题是因为arg1是JSON字符串,而非Hash对象。直接调用arg1[:id]会触发NoMethodError,导致RSpec的block中断言提前失败,后续校验逻辑根本没机会执行,所以看起来第二个条件被忽略了。
方案1:构造预期JSON做精确匹配
先根据Order对象构造出预期的JSON字符串,再用with方法直接匹配两个参数:
it "should invoke send_record_to_kinesis with correct params" do @order = FactoryBot.create(:order) # 构造预期的JSON字符串 expected_params = { id: @order.id, max_budget: @order.budget, startDate: @order.start_date.to_i * 1000, endDate: @order.end_date.to_i * 1000 } expected_json = expected_params.to_json # 验证方法调用参数完全符合预期 expect(KinesisHelper).to receive(:send_record_to_kinesis).with(expected_json, @order.stream) OrderProcesser.post_order(@order) end
方案2:在block中解析JSON后逐一校验
如果需要更灵活的校验(比如忽略字段顺序或兼容额外字段),可以在接收方法的block里先把JSON字符串解析成Hash,再做断言:
it "should invoke send_record_to_kinesis with correct params" do @order = FactoryBot.create(:order) expect(KinesisHelper).to receive(:send_record_to_kinesis) do |json_str, stream| # 校验第二个参数 expect(stream).to eq(@order.stream) # 解析JSON字符串为符号键Hash parsed_json = JSON.parse(json_str, symbolize_names: true) # 逐一校验JSON内容 expect(parsed_json[:id]).to eq(@order.id) expect(parsed_json[:max_budget]).to eq(@order.budget) expect(parsed_json[:startDate]).to eq(@order.start_date.to_i * 1000) expect(parsed_json[:endDate]).to eq(@order.end_date.to_i * 1000) end OrderProcesser.post_order(@order) end
注意事项
- 使用
JSON.parse时,加上symbolize_names: true可以让解析后的Hash使用符号键,和业务代码的构造逻辑保持一致。 - 如果业务代码可能返回格式化后的JSON(带空格、换行),用解析后对比的方式可以避免字符串匹配失败。
内容的提问来源于stack exchange,提问作者Shankar
相关产品推荐
相关产品推荐

