问题
I am trying to figure out the best way to reach 100% test coverage on this class. I have outlined my full spec and I am hoping someone can point me in the right direction. My assumption is stubbing the Oauth2 request would do this, but I can not seem to make that work.
I'm using Rails 4.
Spec
RSpec.describe 'AppOmniAuthentication', type: :concern do
let(:klass) { User }
let(:user) { create(:user) }
let(:user_oauth_json_response) do
unfiltered_oauth_packet = load_json_fixture('app_omni_authentication_spec')
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[:app] = OmniAuth::AuthHash.new(
user_oauth_json_response,
credentials: { token: ENV['APP_CLIENT_ID'], secret: ENV['APP_CLIENT_SECRET'] }
)
end
describe '#from_omniauth' do
let(:app_oauth) { OmniAuth.config.mock_auth[:app] }
it 'returns varying oauth related data for Bigcartel OAuth response' do
data = klass.from_omniauth(app_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 '#refresh_access_token!' do
it 'false if OAuth2 Fails' do
allow(user).to receive(:access_token) { true }
allow(user).to receive(:refresh_access_token!) { false }
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
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
end
describe '#refresh_token!' do
it 'false if no access token' do
allow(user).to receive(:access_token) { false }
expect(user.refresh_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 is saved' do
allow(user).to receive(:access_token) { true }
allow(user).to receive(:refresh_access_token!) { true }
expect(user.refresh_token!).to be_truthy
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
I altered the current spec to:
it 'false if OAuth2 Fails' do
allow(OAuth2::AccessToken).to receive(:access_token) { Class.new }
allow(OAuth2::AccessToken).to receive_message_chain('access_token.refresh!') { raise OAuth2::Error.new('ERROR') }
binding.pry
end
However, now I am getting the following error:
WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled. Unregistered request..
回答1:
It looks like the key is the second line here:
def refresh_access_token!
result = access_token.refresh!
store_token(result)
save
rescue OAuth2::Error
false
end
All your other tests stub access_token
. If you had a test that called the real method, it would cover the missing lines in access_token
, client
, strategy
, and settings
.
Now strategy
,settings
, and client
are all pretty boring: the first two are essentially just constants. And client
doesn't do anything just by initializing it. So calling those should be no big deal. That leaves just access_token
. You can see that that initializes an OAuth2::AccessToken
, whose code is on Github. The initializer is also pretty boring. It just saves the inputs to use later.
So I would stub its refresh!
method: once to return a valid-looking refresh token, and once to raise an OAuth2::Error
. You can use expect_any_instance_of
to do that. If that makes you uneasy you could also stub :new
itself, and have it return your own fake object:
o = expect(OAuth2::AccessToken).to receive(:new) { Object.new }
expect(o).to receive(:refresh!) { raise OAuth2::Error.new("something here") }
It looks like it might be a little bit of a nuisance to construct an OAuth2::Error
since it takes a request object, but I don't see anything too complicated there, just messy.
That should give you 100% coverage!
回答2:
(Posted solution on behalf of the question author).
This is now solved.
describe '#refresh_access_token!' do
it 'false if OAuth2 Fails' do
response = OpenStruct.new(error: 'ERROR')
allow(OAuth2::AccessToken).to receive_message_chain(:new, :refresh!) { raise OAuth2::Error.new(response) }
expect(user.refresh_token!).to be_falsey
end
it 'true if new token' do
allow(OAuth2::AccessToken).to receive_message_chain(:new, :refresh!) { @auth_token }
expect(user.refresh_token!).to be_truthy
end
end
The stubbed token looks like:
@auth_token = OpenStruct.new(token: FFaker::Lorem.characters(50),
refresh_token: FFaker::Lorem.characters(50),
expires_at: 5.days.from_now)
After researching the concept that Paul Jungwirth posted, stubbing the methods in the gem prevented the Webmock alert from firing (as intended) and I could trigger the raise correctly. The raise had to match the gem's response setup as well.
来源:https://stackoverflow.com/questions/38085404/oauth-model-concern-test-coverage-stubs