【发布时间】:2025-12-04 12:00:01
【问题描述】:
我在这样的类中有一个实例方法:
def get(path)
try_request do
RestClient.get(path, headers)
end
end
而try_request方法在代码被抢救时包含了retry,这样在对Unauthorized异常做一些处理后可以重新请求block:
def try_request
tries ||= 2
EMS::Response.new(yield)
rescue RestClient::Unauthorized => e
tries -= 1
if tries.positive?
ems_credentials.update_and_return_token
retry
end
Rails.logger.error "Exception on #{e.class}: #{e.message}"
EMS::Response.new(unauthorized_response)
end
所以我正在尝试通过以下测试来测试此重试:
it 'retries and returns a successful response' do
# allow the RestClient to raise an Unauthorized response
allow(RestClient).to receive(:get).and_raise(RestClient::Unauthorized)
# call the endpoint which would cause the above error
described_class.new.get('/endpoint')
# mock out a successful response:
rest_response = double('rest_response', code: 200, body: { test: 'Test' }.to_json)
# update the RestClient to return the successful response this time
allow(RestClient).to receive(:get).and_return(rest_response)
# retry the request and check the response is as expected:
client_response = described_class.new.get('/endpoint')
expect(client_response.code).to eq(200)
expect(client_response.success?).to be_truthy
expect(client_response.body[:test]).to eq('Test')
expect(client_response).to be_an_instance_of(EMS::Response)
end
这通过并为我提供了该救援的完整代码覆盖率。
但是实际发生的情况是,由于请求引发了错误,我们立即在这一点结束:EMS::Response.new(unauthorized_response) 并且因为在测试的这一点上我没有对 expects 做任何事情然后继续测试(所以考虑所有调用的救援代码),然后我们重新模拟响应并期望,所以我们最终得到一个认为整个块是完全测试代码的测试......但我们没有' t 实际上验证是否发出请求...如果第一次引发异常,它会再次成功调用相同的方法,如果重试发生多次,则 THEN 成功响应或失败。
如何正确测试这三种场景?
1.) 测试如果引发异常,请求会再次重试。
2.) 测试如果第二次尝试通过,我得到一个成功的响应。
3.) 测试如果第二次尝试失败,那么我会得到最终的EMS::Response.new(unauthorized_response) 响应。
希望这是有道理的?我研究过使用should_receive(:retry),但我不知道如何使用它来验证实际相同的代码是否被重新调用,以及我如何验证它只发生一次。
【问题讨论】:
-
我会测试快乐的路径,然后测试如果它一直失败,它会重试几次并给出最终响应。该场景涵盖了中间场景(因为重试只是重复第一个场景——如果它不产生异常,您所涵盖的场景将起作用)。
标签: ruby-on-rails ruby rspec