2016-09-09 2 views
0

Mein Test "should call #logout_all" versagt mitRspec: Objekt nicht Methodenaufruf von Klassenmethode Test

expected: 1 time with any arguments 
received: 0 times with any arguments 

empfangen, aber wenn es User.verify_from_token direkt im rails console nenne, kann ich sehen, dass #logout_all genannt wird (I hinzugefügt eine puts Anweisung #logout_all)

RSpec.describe User, type: :model do 
    describe ".verify_from_token" do 
     let(:user) {FactoryGirl.create(:user, verified: false)} 

     it "should return the user if found" do 
     token = user.to_valid_token 
     expect(User.verify_from_token token).to eq(user) 
     end 

     it "should verify the user" do 
     token = user.to_valid_token 
     User.verify_from_token token 
     expect(user.reload.verified).to eq(true) 
     end 

     it "should call #logout_all" do 
     token = user.to_valid_token 
     expect(user).to receive(:logout_all) 
     User.verify_from_token token 
     end 
    end 
end 


class User < ApplicationRecord 

... 

    def self.verify_from_token token 
    user = from_token token 
    if user 
     user.update_attribute(:verified, true) 
     user.logout_all 
     user 
    else 
     nil 
    end 
    end 

... 
    def logout_all 
    update_attribute(:token_timestamp, Time.now) 
    end 
end 

Wenn ich den Test leicht überarbeiten, es funktioniert gut.

it "should call #logout_all" do 
    token = user.to_valid_token 
    t1 = user.token_timestamp 
    User.verify_from_token token 
    expect(t1 < user.reload.token_timestamp).to eq(true) 
    end 
+0

Bestellung für Ihr 'expect' und' User.verify_from_token Token' ist falsch. Im Moment erwarten Sie, zuerst einen Anruf zu erhalten und dann nur die Methode aufzurufen, die die von Ihnen erwartete Methode aufruft. – Kkulikovskis

+0

Nein, die Implementierung ist korrekt. https://relisapp.com/rspec/rspec-mocks/docs/basics/expecting-messages –

+1

FWIW Ich bevorzuge trotzdem die zweite Version des Tests, da Sie eher das Verhalten als die Implementierung testen. Ich sehe jedoch nicht, warum die ursprüngliche Version versagt; hast du vielleicht einen komischen Code in einem 'before' Block oder in' spec_helper.rb'? Vielleicht gibt die 'User # from_token' Methode das Benutzerobjekt nicht wirklich zurück? Ich müsste eintauchen und es ein wenig mehr debuggen. –

Antwort

1

Das Problem ist, dass Sie Erwartung auf einen Benutzer festgelegt, aber Methodenaufruf geschieht auf andere Benutzer. Dieses.

user = from_token token 

Sie zeigten keine Implementierung von from_token, aber ich bin bereit zu wetten, dass es Benutzer aus der Datenbank lädt. Jetzt kann nur ein Benutzer in der Datenbank vorhanden sein, und logisch repräsentieren diese zwei Variablen die gleiche Entität. Aber physisch sind sie immer noch zwei verschiedene Objekte im Gedächtnis. Natürlich wird die Erwartung nicht erfüllt.

+0

Aber da 'token = user.to_valid_token', würde ich erwarten, dass dies funktioniert ... In der Theorie .... Es hängt alles von den tatsächlichen Implementierungen von' User # to_valid_token' und 'User # from_token' ab –

+0

@SergioTulentsev Guter Fang . 'User.verify_from_token' dekodiert den JWT, zieht die ID heraus und gibt den Benutzer mit' User.find (id) 'zurück. Dann ruft es '# logout_all' für diesen Benutzer auf. – sheepdog

1

Sie haben, dies zu tun:

it "should call #logout_all" do 
    token = user.to_valid_token 
    allow(User).to receive(:from_token).once.with(token).and_return(user) 
    expect(user).to receive(:logout_all) 
    User.verify_from_token token 
end 

Wie zuvor gesagt, das Verfahren auf eine neue Benutzerinstanz aufgerufen wird, nicht derjenige, stellen Sie te Erwartung. Sie müssen die Klassenmethode "from_token" abbrechen, um dasselbe Objekt zurückzugeben, für das Sie Nachrichten erwarten, und nicht eine andere Instanz mit derselben ID aus der Datenbank.

+0

Das ist eigentlich eine sehr schöne Lösung! Ich mag es, dass es testet, ob '# logout_all' aufgerufen wurde, anstatt das * Verhalten * von' # logout_all' zu testen (was an anderer Stelle getestet werden sollte). – sheepdog