簡體   English   中英

使用Rspec存根和模擬在Rails Oauth中實現100%的測試覆蓋率

[英]Achieving 100% test coverage in Rails Oauth using Rspec stubs and mocks

我試圖找出一種存根/模擬訪問令牌調用的方法,以覆蓋用戶令牌過期時調用的方法。 我在這個問題上閱讀的指南越多,我就越困惑。 我不想致電外部提供商,並且我想確認方法報告100%的覆蓋率,以防開發人員修改它們並且它們無法正常工作。 我應該在以下規格中添加什么以使其達到100%的測試目標?

load_json_fixture('omitted_oauth')根據初始Oauth調用返回load_json_fixture('omitted_oauth') JSON夾具。

模型關注

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%

在此處輸入圖片說明

我不確定您可能在哪里呼叫外部提供程序,因此不確定您要存根/模擬什么。

為了使您更接近覆蓋目標,請嘗試為最簡單的模塊方法添加另一個規范:

  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。

無論如何,我認為關鍵是您應該使用allow方法對方法進行存根,這將使您得以逐步了解方法存根。 希望它能有所幫助。

為了refresh_access_token! 您可以通過對user.result方法加一個錯誤,而不是對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

存根和Mo子可能很有趣。 我從這個線程中學到了很多。 這是Rspec 3.4文檔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM