J'ai eu une méthode dans un modèle:
class Article < ActiveRecord::Base
def do_something
end
end
J'ai également eu un test unitaire pour cette méthode:
# 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
Tout allait bien jusqu'à ce que je trouve qu'il est préférable de déplacer cette méthode dans un callback after_save
:
class Article < ActiveRecord::Base
after_save :do_something
def do_something
end
end
Maintenant, tous mes tests sur cette méthode ont échoué. Je dois le réparer en:
do_something
car create
ou save
déclenchera également cette méthode, ou je rencontrerai des actions de base de données en double.create
par build
Utilisez le model.save
général au lieu de l'appel de méthode individuel 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
Le test a réussi, mais ce qui me préoccupe, c’est qu’il ne s’agit plus de la méthode spécifique. L'effet sera mélangé avec d'autres rappels si plus est ajouté .
Ma question est la suivante: existe-t-il un moyen magnifique de tester les méthodes d'instance d'un modèle de manière indépendante pour devenir un rappel?
Les comportements de rappel et de rappel sont des tests indépendants. Si vous souhaitez vérifier un rappel after_save, vous devez le considérer comme deux choses:
Supposons que vous ayez la classe Article
avec de nombreux rappels, voici comment vous testeriez:
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
Cela dissocie vos rappels du comportement. Par exemple, vous pouvez déclencher la même méthode de rappel article.do_something
lorsqu'un autre objet associé est mis à jour, par exemple user.before_save { user.article.do_something }
. Cela va accueillir tous ceux.
Alors, continuez à tester vos méthodes comme d’habitude. S'inquiéter des rappels séparément.
Éditer: fautes de frappe et idées fausses potentielles .__ Éditer: changer "faire quelque chose" pour "déclencher quelque chose"
Vous pouvez utiliser shoulda-callback-matchers pour tester l'existence de vos rappels sans les appeler.
describe Article do
it { should callback(:do_something).after(:save) }
end
Si vous souhaitez également tester le comportement du rappel:
describe Article do
...
describe "#do_something" do
it "gives the article something" do
@article.save
expect(@article).to have_something
end
end
end
Ceci est plus un commentaire qu'une réponse, mais je le mets ici pour la coloration syntaxique ...
Je voulais un moyen de passer les callbacks dans mes tests, c'est ce que j'ai fait. (Cela pourrait aider avec les tests qui ont éclaté.)
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
Dans l’esprit de Sandi Metz et de minimaliste testing , la suggestion de https://stackoverflow.com/a/16678194/2001785 de confirmer l’appel à une méthode éventuellement privée ne me semble pas juste.
Tester un effet secondaire observable publiquement ou confirmer un message de commande sortant me semble plus logique. Christian Rolle a donné un exemple à http://www.chrisrolle.com/fr/blog/activerecord-callback-tests-with-rspec .