2017-12-27 7 views
1

Ich habe ein paar Tests wie diese bekommen:Wie vermeidet man die Verwendung von expect_any_instance_of hammer?

it 'should invite user again' do 
    admin_user = create(:invited_admin_user) 
    expect_any_instance_of(AdminUser).to receive(:invite!).and_return(true) 
    patch :reinvite, params: { id: admin_user.to_param } 
end 

Ich möchte wirklich es so schreiben:

it 'should invite user again' do 
    admin_user = create(:invited_admin_user) 
    expect(admin_user).to receive(:invite!).and_return(true) 
    patch :reinvite, params: { id: admin_user.to_param } 
end 

Aber der Test schlägt fehl, wenn ich das tun. Irgendeine Idee, warum das passieren würde? Ich verwende factory_bot, um die AdminUser-Instanz zu erstellen.

Ich habe versucht, puts Anweisungen in den Test und die Methode einladen, um die ID zu bestätigen.

def invite!(_param1 = AdminUser.new, _param2 = {}) 
    puts 'ID in invite!' + self.id.inspect 
    super(_param1, _param2) 
end 

it 'should invite user again' do 
    admin_user = create(:invited_admin_user) 
    puts 'adminuser created' + admin_user.id.inspect 
    expect(admin_user).to receive(:invite!).and_return(true) 
    patch :reinvite, params: { id: admin_user.to_param } 
end 

Ergebnis

adminuser created7768

ID in einladen! 7768

+0

können Sie bitte fügen Sie erneut Aktion auch hinzu? – Manishh

Antwort

2

Warum möchten Sie Mocks hier in erster Linie verwenden?

it 'should invite user again' do 
    admin_user = create(:invited_admin_user) 
    patch :reinvite, params: { id: admin_user.to_param } 
    expect(admin_user.reload.invited).to eq(true) 
end 

Wenn Sie redundante Anrufe auf die Datenbank vermeiden will, sollte die gesamte Prüfung a) ohne eine echte DB-Objekterstellung (FactoryGirl#build,) b) ohne patch Aufruf geschrieben werden (direkt den jeweiligen Controller-Methode aufrufen,) und c) sich über alles lustig machen, um dazwischen gerufen zu werden.

NB Ich persönlich sehe keinen Grund, Tests zu haben, bei denen alles verspottet wird: Sie unterscheiden sich kaum vom Code selbst. Ich meine, wir könnten sowohl im Test als auch im Code einen Fehler machen und überprüfen, dass patch die Methode des jeweiligen Controllers aufruft: Es ist bereits in Rails-Tests überprüft. Ich versuche immer echte Dinge, wenn anwendbar (wie der Benutzer tatsächlich geändert wurde, anstatt einige Methode aufgerufen wurde.)

2

Das Problem ist, dass der Code den Datensatz neu ist das Finden und es als neues Objekt instanziiert wird, Daher unterscheidet sich das Objekt, das die Nachricht empfängt, von dem in Ihrem Test.

runden dieses tun, um:

it 'should invite user again' do 
    admin_user = create(:invited_admin_user) 
    expect(User).to receive(:find).with(admin_user.id).and_return(admin_user) 
    expect(admin_user).to receive(:invite!).and_return(true) 
    patch :reinvite, params: { id: admin_user.to_param } 
end 

Dies dazwischen nur die User.find Anruf und gibt Ihr Testobjekt eher als das es normalerweise initialisieren würde.

+0

Hmm interessant. Mein Code verwendet nicht tatsächlich find: @admin_users = AdminUser.deleted.where (E-Mail: admin_user_params [: email]) (und dann iteriert es über jedes). Es scheint ein bisschen hacky zu sein, um .find sowieso zu übersteuern. Ist das eine empfohlene Praxis oder versuche ich nur das Falsche zu tun? – jeznag

+1

Es ist nicht ideal, eine Erwartung auf 'find' zu setzen, aber wenn du expect_any_instance_of nicht verwenden willst, gibt es sehr wenig Auswahl. Es gibt ein Argument, dass Ihre Funktion, die 'invite!' Aufruft, ein Implementierungsdetail ist, und stattdessen könnten Sie das Endergebnis überprüfen, wie eine E-Mail ausgelöst wird oder ein 'invite_sent_at'-Zeitstempel aktualisiert wird, wenn diese Dinge passieren. Das heißt, Ihre Frage war ziemlich spezifisch, also gab ich die Antwort darauf. Hilft Ihnen eine dieser Alternativen? – TomDunning

Verwandte Themen