2016-06-15 1 views
0

J'essaye de trouver un moyen de boucher/simuler les appels de jetons d'accès pour fournir une couverture aux méthodes appelées quand le jeton d'un utilisateur a expiré. Plus je lis de guides sur ce sujet, plus je me sens confus. Je ne veux pas appeler le fournisseur externe, et je veux confirmer que les méthodes rapportent 100% de couverture au cas où un développeur les modifie et qu'elles fonctionnent mal. Que dois-je ajouter à la spécification ci-dessous pour l'atteindre à 100%?Atteindre 100% de couverture de test dans Rails Oauth en utilisant des stubs et des mocks Rspec

Le load_json_fixture('omitted_oauth') introduit un appareil JSON basé sur ce que l'appel Oauth initial retourne.

Modèle Concern

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 

enter image description here

MISE À JOUR

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 

=>j'ai pu arriver à 94,12% avec ces mises à jour

enter image description here

+0

Ceci est un peu trop grand et large pour moi de lire et de comprendre. Pouvez-vous indiquer une ou plusieurs lignes que vous voulez couvrir? –

+0

@DaveSchweisguth J'ai mis à jour la question avec des problèmes de couverture. –

+0

Donc, votre image, il y a votre couverture de test? La méthode 'token_expired?' Est-elle couverte? En d'autres termes, est-ce que 'Time.now> token_expires_at' est vert? Ce n'est pas visible à votre image. – sealocal

Répondre

0

Je ne sais pas où vous pourriez appellerez le fournisseur externe, donc je ne suis pas sûr de ce que vous vouloir talonner/se moquer.

Pour vous obtenir un peu plus près de votre objectif de couverture, essayez d'ajouter une autre spécification pour vos plus simples méthodes du module:

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 

Cet exemple se sent un peu inutile puisque votre méthode access_token semble qu'il ne reviendra jamais faux. Je m'attendrais à ce que votre méthode access_token retournera toujours un objet, ou une erreur, de sorte que votre méthode refresh_token! ne rencontrerait jamais une condition fausse dans le ternaire. Peut-être que vous devriez plutôt sauver et retourner faux. Quoi qu'il en soit, je pense que le point est que vous devriez talonner la méthode avec la méthode allow, et cela vous aidera à trouver vos talons de méthode. J'espère que cela aide un peu.

Pour refresh_access_token! vous pouvez tester la méthode en battant la méthode user.result avec une erreur, et ne pas talonner pour le résultat "réussi" de la méthode 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 
+0

Je suis dans le dernier tronçon + Je viens de faire quelques grands modifications Réflexions sur l'obtention de 100%? –

+0

Il semble que pour chaque test qui appelle 'refresh_access_token!', Vous avez soit stubbed cette méthode, soit écrasé le 'user.result', donc la méthode' refresh_access_token! 'Ne s'exécute jamais complètement. Vous pourriez écrire un bloc describe pour le 'refresh_access_token!'. Peut-être que pour un test dans le bloc de description, vous pourriez tester la condition réussie et pour l'autre, vous pouvez remplacer le résultat par l'erreur que vous secourez. – sealocal

+0

ok, peut-être que je suis perdu ici @sealocal, comment pourrais-je écrire ce que vous dites? –

-1

(solution Posté le nom de l'auteur de la question).

Cela fonctionne maintenant.Avec le réglage de la spécification suivante stubbing la chaîne de méthode que je suis en mesure d'obtenir un appel réussi de true pour la méthode:

def refresh_access_token! 
    result = access_token.refresh! 
    store_token(result) 
    save 
    rescue OAuth2::Error 
    false 
    end 

La spécification terminée qui m'a poussé à 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 et Mocks peut être amusant. J'ai appris une tonne de ce fil. Voici les Rspec 3.4 docs on this.