25

hatte ich eine Methode in einem Modell:Wie kann man die Callback-Methode des Modells unabhängig testen?

class Article < ActiveRecord::Base 
    def do_something 
    end 
end 

ich auch einen Komponententest für diese Methode hatte:

# spec/models/article_spec.rb 
describe "#do_something" do 
    @article = FactoryGirl.create(:article) 
    it "should work as expected" do 
    @article.do_something 
    expect(@article).to have_something 
    end 
    # ...several other examples for different cases 
end 

Alles war gut, bis ich es besser gefunden diese Methode in einen after_save Rückruf bewegen :

class Article < ActiveRecord::Base 
    after_save :do_something 

    def do_something 
    end 
end 

Jetzt alle meine Tests über diese Methode gebrochen. Ich habe es beheben von:

  • Keine spezifischen Aufruf do_something weil create oder save wird diese Methode auch auslösen, oder ich werde doppelte db Aktionen treffen.
  • ändern create zu build
  • -Test respond_to
  • Verwenden allgemeinen model.save anstelle einzelner Methodenaufruf model.do_something

    describe "#do_something" do 
        @article = FactoryGirl.build(:article) 
        it "should work as expected" do 
        expect{@article.save}.not_to raise_error 
        expect(@article).to have_something 
        expect(@article).to respond_to(:do_something) 
        end 
    end 
    

Der Test bestanden, aber meine Sorge ist es nicht mehr geht um die spezifische Methode. Der Effekt wird gemischt mit anderen Rückrufen sein, wenn mehr hinzugefügt.

Meine Frage ist, gibt es eine schöne Art und Weise Instanzmethoden des Modells unabhängig zu testen, ob ein Rückruf zu werden?

+0

Es ist nicht klar, warum Ihr ursprünglicher Ansatz noch nicht für die Prüfung arbeiten. Unit testet die Methode direkt und testet einfach, dass sie als Callback unabhängig aufgerufen wird. Fehle ich etwas oder gibt es etwas, das dir an diesem Ansatz nicht gefällt? –

+0

@AndrewHubbs, danke für deine Frage. Der Grund ist diese geänderte Methode db. Zum Beispiel wird dieser Artikel der Kategorie "Rails" zugewiesen. Nach dem Refacoring auf Callback, wenn ich FactoryGirl.create aufrufe, wird dieser Callback wirksam und Artikel der Kategorie "Rails" zuweisen. Wenn ich diese Methode innerhalb des Tests erneut aufrufe, wird ein Fehler angezeigt, da sie bereits zugewiesen ist. –

Antwort

54

Callback- und Callback-Verhalten sind unabhängige Tests. Wenn Sie einen after_save Callback überprüfen möchten, müssen Sie sich zwei Dinge vorstellen:

  1. Wird der Callback für die richtigen Ereignisse ausgelöst?
  2. Funktioniert die aufgerufene Funktion richtig?

Angenommen, Sie die Article Klasse mit vielen Rückrufe haben, ist dies, wie Sie sich testen:

class Article < ActiveRecord::Base 
    after_save :do_something 
    after_destroy :do_something_else 
    ... 
end 

it "triggers do_something on save" do 
    expect(@article).to receive(:do_something) 
    @article.save 
end 

it "triggers do_something_else on destroy" do 
    expect(@article).to receive(:do_something_else) 
    @article.destroy 
end 

it "#do_something should work as expected" do 
    # Actual tests for do_something method 
end 

Dies entkoppelt Ihre Rückrufe von Verhalten. Zum Beispiel könnten Sie dieselbe Callback-Methode article.do_something auslösen, wenn ein anderes verwandtes Objekt aktualisiert wird, etwa user.before_save { user.article.do_something }. Dies wird all denen entgegenkommen.

Also, testen Sie Ihre Methoden wie gewohnt. Sorgen Sie sich für die Rückrufe separat.

Edit: Tippfehler und mögliche Missverständnisse Edit: Änderung „etwas tun“, um „auslösen etwas“

+0

+1 für "entkoppelt Ihre Rückrufe von Verhalten." RDX, was ist deine Empfehlung für "# Tatsächliche Tests für Do_Something-Methode"? Benutze meine Methode später (Test Gesamtverhalten durch "Speichern" des Objekts)? –

+0

Nur die tatsächlichen Tests für 'do_something' Methode, etwas wie die erste rspec in Ihrem Beitrag:' @ article.do_something; erwarten (@article) .to have_something. Grundsätzlich ist 'save' eine größere Methode, die möglicherweise eine Menge von vorher_saves, after_saves usw. auslösen kann. Aber wenn Sie alle einzelnen Methoden getestet haben und Sie wissen, dass jede Methode ihre Aufgabe richtig macht, dann müssen Sie nur schreiben sehr kurze und kurze Tests für die Methoden save/before_save/after_save/.... Komponieren Sie größere Funktionen aus gut getesteten kleineren Funktionen :) – Subhas

+0

durch Zufall, haben Sie irgendwelche Gedanken auf http://StackOverflow.com/Questions/35950470/rails-FactoryGirl-Trait-Verbindung-mit-Modell-nach-Create-Callback -nicht-Einstellung –

0

Dies ist eher ein Kommentar als eine Antwort, aber ich habe es hier für die Syntax-Hervorhebung ...

ich einen Weg, um die Rückrufe in meinen Tests überspringen wollte, ist es das, was ich tat. (Dies könnte bei den fehlgeschlagenen Tests hilfreich sein.)

class Article < ActiveRecord::Base 
    attr_accessor :save_without_callbacks 
    after_save :do_something 

    def do_something_in_db 
    unless self.save_without_callbacks 
     # do something here 
    end 
    end 
end 

# spec/models/article_spec.rb 
describe Article do 
    context "after_save callback" do 
    [true,false].each do |save_without_callbacks| 
     context "with#{save_without_callbacks ? 'out' : nil} callbacks" do 
     let(:article) do 
      a = FactoryGirl.build(:article) 
      a.save_without_callbacks = save_without_callbacks 
     end 
     it do 
      if save_without_callbacks 
      # do something in db 
      else 
      # don't do something in db 
      end 
     end 
     end 
    end 
    end 
end 
+0

Teddy, danke für deine Antwort oder Kommentar? :) Ich fürchte, die Kosten sind auf diese Weise ein bisschen hoch, mit etwas zusätzlichen Code im Test, und Änderung des Modells, um sich bewusst an den Test anzupassen. –

+0

Ja, ich musste auch die db säen, wo das Überspringen von Rückrufen eine Voraussetzung war, so dass ich in meinem Fall mehr Nutzen hatte. – Teddy

+0

Es gibt eine skip_callback-Methode dafür: https://github.com/rails/rails/blob/b894b7b90a6aced0e78ab84a45bf1c75c871bb2d/activesupport/lib/active_support/callbacks.rb#L620 –

13

Sie shoulda-callback-matchers verwenden können Existenz Ihres Rückrufe zu testen, ohne sie zu nennen.

describe Article do 
    it { should callback(:do_something).after(:save) } 
end 

Wenn Sie auch das Verhalten des Callback testen wollen:

describe Article do 
    ... 

    describe "#do_something" do 
    it "gives the article something" do 
     @article.save 
     expect(@article).to have_something 
    end 
    end 
end 
+0

toll bearbeiten @dennis! Vielen Dank :) –

-1
describe "#do_something" do 

it "gives the article something" do 

    @article = FactoryGirl.build(:article) 

    expect(@article).to have_something 

@article.save 
end 

end