如何在RSpec中修改Stub实现请求先返回422再返回200
解决WebMock Stub覆盖问题:两次相同URL请求返回不同响应
你遇到的问题是WebMock的常见行为:当你定义多个匹配相同请求规则的stub时,最后定义的那个会覆盖前面所有的。所以你的save_payer_refund_fail_request(422)覆盖了save_payer_refund_success_request(200),导致两次请求都返回422,第二个断言自然失败。
下面给你几个可行的解决方案,按推荐程度排序:
方案1:使用序列响应(最简单直接)
WebMock允许给同一个stub定义一系列响应,每次请求会按顺序返回下一个结果。这样第一次请求返回422触发模态框,第二次返回200完成保存,完美匹配你的测试流程:
scenario 'successfully saves refund when user clicks yes continue on similar refund', js: true do # 定义一个stub,先返回422,再返回200 save_payer_refund_request = stub_request(:post, /remittances/) .to_return([ { status: 422, body: {error: "SIMILAR_REMITTANCE"}.to_json }, { status: 200 } ]) visit record_refund_check_by_payer_remittances_path # 填写表单的步骤... page.find_by_id('refund_record').click wait_for_ajax # 断言第一次请求已触发 expect(save_payer_refund_request).to have_been_made.once expect(page).to have_content "A refund from this payer with this number with the amount of $#{@refundTotalAmount} and date of #{@EFTCheckDate} already exists in Value-Based Reimbursement. Are you sure you want to record this refund?" page.find_by_id('save-refund-confirmation-button').click wait_for_ajax # 断言第二次请求已触发 expect(save_payer_refund_request).to have_been_made.twice expect(page).to have_content "Refund check #{@checkNumber} to payer has been saved." end
方案2:通过请求体区分两次请求(更精准)
如果两次POST请求的请求体内容不同(比如点击确认按钮时会额外发送confirm: true这类参数),你可以用with方法给两个stub设置不同的匹配条件,这样它们就不会互相覆盖:
scenario 'successfully saves refund when user clicks yes continue on similar refund', js: true do # 匹配第一次请求(不带确认参数) save_payer_refund_fail_request = stub_request(:post, /remittances/) .with(body: ->(body) { # 根据实际请求体逻辑调整,比如检查是否不含确认标识 parsed_body = JSON.parse(body) !parsed_body.key?('confirm') || parsed_body['confirm'] != true }) .to_return(status:422, body: {error: "SIMILAR_REMITTANCE"}.to_json) # 匹配第二次请求(带确认参数) save_payer_refund_success_request = stub_request(:post, /remittances/) .with(body: ->(body) { parsed_body = JSON.parse(body) parsed_body['confirm'] == true }) .to_return(status: 200) visit record_refund_check_by_payer_remittances_path # 填写表单的步骤... page.find_by_id('refund_record').click wait_for_ajax expect(save_payer_refund_fail_request).to have_been_made expect(page).to have_content "A refund from this payer with this number with the amount of $#{@refundTotalAmount} and date of #{@EFTCheckDate} already exists in Value-Based Reimbursement. Are you sure you want to record this refund?" page.find_by_id('save-refund-confirmation-button').click wait_for_ajax expect(save_payer_refund_success_request).to have_been_made expect(page).to have_content "Refund check #{@checkNumber} to payer has been saved." end
方案3:动态响应(最灵活)
你还可以用lambda来动态决定返回的响应,比如根据请求次数或者请求内容返回不同结果:
scenario 'successfully saves refund when user clicks yes continue on similar refund', js: true do save_payer_refund_request = stub_request(:post, /remittances/) .to_return do |request| # 根据已调用的次数返回不同响应 if save_payer_refund_request.requests.size == 1 { status: 422, body: {error: "SIMILAR_REMITTANCE"}.to_json } else { status: 200 } end end # 后续测试步骤和方案1一致... end
为什么原来的代码会失败?
WebMock的stub匹配规则是最后定义的stub优先级最高,当两个stub匹配同一个请求(都是POST到/remittances),后面的stub会完全覆盖前面的,所以所有请求都会使用422的响应,导致第二个断言失败。
内容的提问来源于stack exchange,提问作者BarflyII37
相关产品推荐
相关产品推荐

