使用 RSpec 测试 Retry RestClient
我正在使用 Oauth,所以我所做的就是在 User
表中存储 access_token
和 refresh token
,我创建了一些类来执行此操作。在 Create
类中,我执行代码的正常功能(在集成上创建记录)。 access_token 将于 1 小时后过期,因此我决定执行 Refresh.new(user).call,而不是安排一个活动作业来刷新该令牌。 请求新的 access_token
和 refresh_token
。
我知道该代码有效,因为我已经进行了现场测试,并且当 access_token 过期时我得到了新的令牌。但我想为此做一个 rspec 测试。
我的rspec 测试的一部分:。
context 'when token is expired' do
it 'request a refresh token and retry' do
old_key = user.access_token
allow(RestClient)
.to receive(:post)
.and_raise(RestClient::Unauthorized).once
expect { Create.new.call }.to change { user.reload.access_token }.from(old_key)
end
end
这是响应:
(RestClient).post(# data)
expected: 1 time with any arguments
received: 2 times with arguments: (# data)
这是我的代码:
Create.rb
class Create
def initialize(user)
@user = user
@refresh_token = user&.refresh_token
@access_token = user&.access_token
@logger = Rails.logger
@message = Crm::Message.new(self.class, 'User', user&.id)
end
def call
# validations
create_contact
rescue RestClient::Unauthorized => ex
retry if Refresh.new(user).call
rescue RestClient::ExceptionWithResponse => ex
logger.error(@message.api_error(ex))
raise
end
private
attr_reader :user, :logger, :access_token, :refresh_token
def create_contact
response = RestClient.post(
url, contact_params, contact_headers
)
logger.info(@message.api_response(response))
end
end
Refresh.rb
class Refresh
def initialize(user)
@user = user
@refresh_token = user&.refresh_token
@access_token = user&.access_token
@logger = Rails.logger
@message = Crm::Message.new(self.class, 'User', user&.id)
end
def call
# validations
refresh_authorization_code
end
def refresh_authorization_code
response = RestClient.post(url, authorization_params)
logger.info(@message.api_response(response))
handle_response(response)
end
private
attr_reader :user, :logger, :access_token, :refresh_token
def handle_response(response)
parsed = JSON.parse(response)
user.update!(access_token: parsed[:access_token], refresh_token: parsed[:refresh_token])
end
end
我也尝试使用类似的东西这是来自这里
errors_to_raise = 2
allow(RestClient).to receive(:get) do
return rest_response if errors_to_raise <= 0
errors_to_raise -= 1
raise RestClient::Unauthorized
end
# ...
expect(client_response.code).to eq(200)
但我不知道如何正确处理。
I'm using Oauth so what I do is store access_token
and refresh token
at User
table, I create some classes to do this. In the Create
class I do the normal functionality of the code (create records on the integration). The access_token
expire at 1 hour, so intead of schedule an active job
to refresh that token at that time I decided to do Refresh.new(user).call
to request a new access_token
and refresh_token
.
I know that code works, because I've tested on live and I'm getting the new token when the access_token is expired. But I want to do a rspec test for this.
part of my rspec test:.
context 'when token is expired' do
it 'request a refresh token and retry' do
old_key = user.access_token
allow(RestClient)
.to receive(:post)
.and_raise(RestClient::Unauthorized).once
expect { Create.new.call }.to change { user.reload.access_token }.from(old_key)
end
end
This is the response:
(RestClient).post(# data)
expected: 1 time with any arguments
received: 2 times with arguments: (# data)
This is my code:
Create.rb
class Create
def initialize(user)
@user = user
@refresh_token = user&.refresh_token
@access_token = user&.access_token
@logger = Rails.logger
@message = Crm::Message.new(self.class, 'User', user&.id)
end
def call
# validations
create_contact
rescue RestClient::Unauthorized => ex
retry if Refresh.new(user).call
rescue RestClient::ExceptionWithResponse => ex
logger.error(@message.api_error(ex))
raise
end
private
attr_reader :user, :logger, :access_token, :refresh_token
def create_contact
response = RestClient.post(
url, contact_params, contact_headers
)
logger.info(@message.api_response(response))
end
end
Refresh.rb
class Refresh
def initialize(user)
@user = user
@refresh_token = user&.refresh_token
@access_token = user&.access_token
@logger = Rails.logger
@message = Crm::Message.new(self.class, 'User', user&.id)
end
def call
# validations
refresh_authorization_code
end
def refresh_authorization_code
response = RestClient.post(url, authorization_params)
logger.info(@message.api_response(response))
handle_response(response)
end
private
attr_reader :user, :logger, :access_token, :refresh_token
def handle_response(response)
parsed = JSON.parse(response)
user.update!(access_token: parsed[:access_token], refresh_token: parsed[:refresh_token])
end
end
Also I tried using something like this from here
errors_to_raise = 2
allow(RestClient).to receive(:get) do
return rest_response if errors_to_raise <= 0
errors_to_raise -= 1
raise RestClient::Unauthorized
end
# ...
expect(client_response.code).to eq(200)
but I don't know how handle it propertly.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的测试调用
RestClient.post
两次,首先在Create
中调用,然后在Retry
中再次调用。但你只嘲笑了一个电话。您需要模拟这两个调用。第一个调用引发异常,第二个调用返回成功结果。我们可以通过
ordered
...但是,这对代码的工作原理做出了很多假设,并且没有其他任何东西调用
RestClient.post
。更强大的方法是使用
with
来指定具有特定参数的响应,并验证传递的参数是否正确。但这仍然对代码的工作原理做出了很多假设,您需要做出适当的响应。
更好的方法是专注于您正在测试的内容:当创建调用收到未经授权的异常时,它会尝试刷新并再次执行调用。此单元测试不必还测试
Refresh#call
是否有效,只需测试Create#call
调用它即可。您不需要让RestClient.post
引发异常,只需Create#create_contact
就可以。并且还可以测试重试失败的时间。这些可以组合在一起。
Your test calls
RestClient.post
twice, first inCreate
then again inRetry
. But you only mocked one call. You need to mock both calls. The first call raises an exception, the second responds with a successful result.We could do this by specifying an order with
ordered
...However, this makes a lot of assumptions about exactly how the code works, and that nothing else calls
RestClient.post
.More robust would be to use
with
to specify responses with specific arguments, and also verify the correct arguments are being passed.But this still makes a lot of assumptions about exactly how the code works, and you need to make a proper response.
Better would be to focus in on exactly what you're testing: when the create call gets an unauthorized exception it tries to refresh and does the call again. This unit test doesn't have to also test that
Refresh#call
works, just thatCreate#call
calls it. You don't need to haveRestClient.post
raise an exception, just thatCreate#create_contact
does.And you can also test when the retry fails. These can be combined together.