使用Rspec存根和模拟在Rails Oauth中实现100%的测试覆盖率
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Rspec存根和模拟在Rails Oauth中实现100%的测试覆盖率相关的知识,希望对你有一定的参考价值。
我试图找出一种方法来存根/模拟访问令牌调用,以便为用户令牌过期时调用的方法提供覆盖。我在这个问题上阅读的指南越多,我就越感到困惑。我不想调用外部提供程序,我想确认方法报告100%覆盖率,以防开发人员修改它们并且它们工作不正确。我应该在下面的规范中添加什么才能达到100%的测试目标?
load_json_fixture('omitted_oauth')
根据初始Oauth调用返回的内容引入JSON fixture。
模范关注
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 Spec
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
UPDATE
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%
我不确定你可能在哪里调用外部提供程序,所以我不确定你想要什么存根/模拟。
为了让您更接近覆盖目标,请尝试为最简单的模块方法添加另一个规范:
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!
方法永远不会遇到三元组中的错误条件。也许你应该反而拯救并返回虚假。
无论如何,我认为关键是你应该使用allow
方法来存储方法,这样你就可以找到你的方法存根。希望它有所帮助。
对于refresh_access_token!
,您可以通过将user.result
方法存在错误来对该方法进行单元测试,而不是对refresh_access_token!
方法的“成功”结果进行存根。
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
(代表问题作者发布解决方案)。
这现在正在运作。通过以下规范调整来截断方法链,我能够成功调用true
用于该方法:
def refresh_access_token!
result = access_token.refresh!
store_token(result)
save
rescue OAuth2::Error
false
end
完成的规范将我推向了100%
it 'true when refreshed' do
auth_token = OpenStruct.new(token: FFaker::Lorem.characters(50),
refresh_token: FFaker::Lorem.characters(50),
expires_at: 5.days.from_now)
allow(user).to receive_message_chain('access_token.refresh!') { auth_token }
expect(user.refresh_access_token!).to be_truthy
end
Stubs和Mocks可以很有趣。我从这个帖子中学到了很多东西。这是Rspec 3.4 docs on this。
以上是关于使用Rspec存根和模拟在Rails Oauth中实现100%的测试覆盖率的主要内容,如果未能解决你的问题,请参考以下文章
在 rspec 中为 API 规范存根protect_from_forgery
模拟 Rails.env.development?使用 rspec
Ruby on Rails:RSpec 和 Acts As Audited(在测试时禁用acts_as_audited?)