问题描述
我正在尝试找出一种对访问令牌调用进行存根/模拟的方法,以覆盖用户令牌过期时所调用的方法.我在这个问题上阅读的指南越多,我就越困惑.我不想致电外部提供商,并且我想确认方法报告100%的覆盖率,以防开发人员修改它们并且它们无法正常工作.我应该在以下规格中添加些什么,以使其达到我们100%的测试目标?
I am trying to figure out a way to stub/mock the access token calls to provide coverage to methods called when a user's token has expired. The more guides I read on this issue the more I get confused. I do not want to call the external provider, and I want to confirm the methods report 100% coverage in case a developer modifies them and they work incorrectly. What should I add to the spec below to make it reach our testing goal of 100%?
load_json_fixture('omitted_oauth')
根据初始Oauth调用返回的内容引入JSON夹具.
The load_json_fixture('omitted_oauth')
brings in a JSON fixture based on what the initial Oauth call returns.
模型关注
module OmittedOmniAuthentication
extend ActiveSupport::Concern
module ClassMethods
def from_omniauth(auth)
Rails.logger.debug auth.inspect
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
setup_user(user, auth)
end
end
def setup_user(user, auth)
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.customer_ids = auth.extra.raw_info.customer_ids
user.store_token(auth.credentials)
end
end
def refresh_token!
access_token ? refresh_access_token! : false
end
def refresh_access_token!
result = access_token.refresh!
store_token(result)
save
rescue OAuth2::Error
false
end
def settings
@settings ||= Devise.omniauth_configs[:omitted].strategy
end
def strategy
@strategy ||= OmniAuth::Strategies::Omitted.new(nil, settings.client_id, settings.client_secret, client_options: settings.client_options)
end
def client
@client ||= strategy.client
end
def access_token
OAuth2::AccessToken.new(client, token, refresh_token: refresh_token)
end
def store_token(auth_token)
self.token = auth_token.token
self.refresh_token = auth_token.refresh_token
self.token_expires_at = Time.at(auth_token.expires_at).to_datetime
end
def token_expired?
Time.now > token_expires_at
end
end
Rspec规格
RSpec.describe 'OmittedOmniAuthentication', type: :concern do
let(:klass) { User }
let(:user) { create(:user) }
let(:user_oauth_json_response) do
unfiltered_oauth_packet = load_json_fixture('omitted_oauth')
unfiltered_oauth_packet['provider'] = unfiltered_oauth_packet['provider'].to_sym
unfiltered_oauth_packet['uid'] = unfiltered_oauth_packet['uid'].to_i
unfiltered_oauth_packet
end
before do
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:omitted] = OmniAuth::AuthHash.new(
user_oauth_json_response,
credentials: { token: ENV['OMITTED_CLIENT_ID'], secret: ENV['OMITTED_CLIENT_SECRET'] }
)
end
describe "#from_omniauth" do
let(:omitted_oauth){ OmniAuth.config.mock_auth[:omitted] }
it 'returns varying oauth related data for Bigcartel OAuth response' do
data = klass.from_omniauth(omitted_oauth)
expect(data[:provider]).to eq(user_oauth_json_response['provider'].to_s)
expect(data[:uid]).to eq(user_oauth_json_response['uid'].to_s)
expect(data[:email]).to eq(user_oauth_json_response['info']['email'])
expect(data[:customer_ids]).to eq(user_oauth_json_response['extra']['raw_info']['customer_ids'])
end
end
describe '#token expired?' do
it 'true if valid' do
expect(user.token_expired?).to be_falsey
end
it 'false if expired' do
user.token_expires_at = 10.days.ago
expect(user.token_expired?).to be_truthy
end
end
end
更新
describe '#refresh_access_token!' do
it 'false if OAuth2 Fails' do
allow(user).to receive(:result).and_raise(OAuth2::Error)
expect(user.refresh_access_token!).to be_falsey
end
it 'false if refresh fails' do
allow(user).to receive(:access_token) { true }
allow(user).to receive(:refresh_access_token!) { false }
expect(user.refresh_token!).to be_falsey
end
it 'true if new token' do
allow(user).to receive(:access_token) { true }
allow(user).to receive(:refresh_access_token!) { true }
expect(user.refresh_token!).to be_truthy
end
it 'true when refreshed' do
allow(user).to receive(:access_token) { true }
allow(user).to receive(:refresh_access_token!) { true }
allow(user).to receive(:store_token) { true }
allow(user).to receive(:save) { true }
expect(user.refresh_access_token!).to be_truthy
end
end
=>
这些更新使我能够达到94.12%
推荐答案
我不确定您可能在哪里呼叫外部提供程序,因此我不确定您要存根/模拟什么.
I'm not sure where you might be calling the external provider, so I'm not sure what you want to stub/mock.
要使您更接近覆盖目标,请尝试为最简单的模块方法添加另一个规范:
To get you a little closer to your coverage goal, try adding another spec for your simplest module methods:
describe '#refresh_token!' do
it 'is true if there is an access_token' do
if !user.access_token?
expect(user.refresh_token!).to be_truthy
end
end
# Do you have factories or fixtures set up that can force
# #access_token? to be falsey?
it 'is false if there is no access_token' do
if !user.access_token?
expect(user.refresh_token!).to be_falsey
end
end
# Maybe you want to set the falsey value for the access_token
# as you have have for the value of token_expires_at in
# your #token_expired? test.
it 'is false if there is no access_token' do
# You should be able to force the method to return a false
# value (stub the method) with this line
allow(user).to receive(:access_token) { false }
expect(user.refresh_token!).to be_falsey
end
end
由于您的access_token
方法似乎永远不会返回false,因此该示例显得有些不必要.我希望您的access_token
方法将始终返回对象或错误,因此您的refresh_token!
方法将永远不会在三进制中遇到虚假条件.也许您应该改为抢救并返回false.
This example feels a little unnecessary since your access_token
method appears that it will never return false. I would expect that your access_token
method will always return an object, or an error, so your refresh_token!
method would never encounter a falsey condition in the ternary. Maybe you should instead rescue and return false.
无论如何,我认为关键是您应该使用allow
方法对方法进行存根,这将使您逐步了解方法存根.希望它能有所帮助.
Regardless, I think the point is that you should stub the method with the allow
method, and that will get you on your way to figuring out your method stubs. Hope it helps somewhat.
对于refresh_access_token!
,您可以通过对错误的user.result
方法进行存根而不是对refresh_access_token!
方法的成功"结果进行存根来对方法进行单元测试.
For refresh_access_token!
you can unit test the method by stubbing the user.result
method with an error, and not stubbing for the "successful" result of the refresh_access_token!
method.
describe '#refresh_access_token!' do
it 'it returns true when refreshed' do
# The successful control flow path for this method
# is to save the user and return true.
# I suppose this would happen smoothly in your tests and app.
expect(user.refresh_access_token!).to be_truthy
end
it 'returns false when an OAuth2 Error is rescued' do
# To force the case that you receive an OAuth2 Error,
# stub the user's access_token return value with the Error
# The refresh_access_token! method should then rescue the error
# and cover the false return value of the method
allow(user).to receive(:access_token) { OAuth2::Error }
expect(user.refresh_access_token!).to be_falsey
end
end
这篇关于使用Rspec存根和模拟在Rails Oauth中实现100%的测试覆盖率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!