Ich hatte eine Methode in einem Modell:
class Article < ActiveRecord::Base
def do_something
end
end
Ich hatte auch einen Komponententest für diese Methode:
# 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 in Ordnung, bis ich herausfand, dass es besser ist, diese Methode in einen after_save
-Callback zu verschieben:
class Article < ActiveRecord::Base
after_save :do_something
def do_something
end
end
Nun sind alle meine Tests über diese Methode gebrochen. Ich muss es reparieren durch:
do_something
, da create
oder save
diese Methode ebenfalls auslösen wird oder ich doppelte Db-Aktionen treffe.create
in build
ändernVerwenden Sie general model.save
anstelle des einzelnen Methodenaufrufs 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 ist bestanden, aber meine Sorge ist nicht mehr die spezifische Methode. Der Effekt wird gemischt mit anderen Rückrufen sein, wenn hinzugefügt wird.
Meine Frage ist, gibt es eine schöne Möglichkeit, die Instanzmethoden des Modells unabhängig zu testen, die zu einem Rückruf werden?
Callback- und Callback-Verhalten sind unabhängige Tests. Wenn Sie einen after_save-Rückruf prüfen möchten, müssen Sie ihn als zwei Dinge betrachten:
Angenommen, Sie haben die Klasse Article
mit vielen Rückrufen. So würden Sie Folgendes 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 vom Verhalten. Sie könnten beispielsweise dieselbe Callback-Methode article.do_something
auslösen, wenn ein anderes verwandtes Objekt aktualisiert wird, beispielsweise user.before_save { user.article.do_something }
. Dies wird all diese aufnehmen.
Testen Sie Ihre Methoden also wie gewohnt. Sorgen Sie sich separat um die Rückrufe.
Edit: Tippfehler und mögliche Missverständnisse Edit: "etwas tun", um "etwas auszulösen"
Sie können shoulda-callback-matchers verwenden, um die Existenz Ihrer Rückrufe zu testen, ohne sie anzurufen.
describe Article do
it { should callback(:do_something).after(:save) }
end
Wenn Sie auch das Verhalten des Rückrufs testen möchten:
describe Article do
...
describe "#do_something" do
it "gives the article something" do
@article.save
expect(@article).to have_something
end
end
end
Dies ist mehr ein Kommentar als eine Antwort, aber ich habe es hier für das Syntax-Highlighting eingefügt ...
Ich wollte eine Möglichkeit, die Rückrufe in meinen Tests zu überspringen. Dies ist, was ich getan habe. (Dies kann bei den durchgeführten Tests helfen.)
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
Im Geiste von Sandi Metz und minimalistischem testing scheint mir der Vorschlag in https://stackoverflow.com/a/16678194/2001785 , um den Aufruf einer möglicherweise privaten Methode zu bestätigen, nicht richtig.
Das Prüfen eines öffentlich beobachtbaren Nebeneffekts oder das Bestätigen einer ausgehenden Befehlsnachricht ist für mich sinnvoller. Christian Rolle gab ein Beispiel an http://www.chrisrolle.com/de/blog/activerecord-callback-tests-with-rspec .