2008-08-28 13 views

Répondre

35

Vous êtes à droite suivi, mais j'ai rencontré un certain nombre d'erreurs de message inattendues frustrantes lors de l'utilisation rSpec, les observateurs et les objets fictifs. Quand je suis en train de tester mon modèle, je ne veux pas avoir à gérer le comportement d'observateur dans mes attentes de message.

Dans votre exemple, il n'y a pas vraiment de bonne façon de spécifier "set_status" sur le modèle sans savoir ce que l'observateur va lui faire.

Par conséquent, je tiens à utiliser le "No Peeping Toms" plugin. Compte tenu de votre code ci-dessus et en utilisant le No Peeping plug-in Toms, je spec le modèle comme celui-ci:

describe Person do 
    it "should set status correctly" do 
    @p = Person.new(:status => "foo") 
    @p.set_status("bar") 
    @p.save 
    @p.status.should eql("bar") 
    end 
end 

Vous pouvez spec votre code de modèle sans avoir à se soucier que il y a un observateur là-bas qui va entrer et battre votre valeur. Vous souhaitez que spec séparément dans les person_observer_spec comme celui-ci:

describe PersonObserver do 
    it "should clobber the status field" do 
    @p = mock_model(Person, :status => "foo") 
    @obs = PersonObserver.instance 
    @p.should_receive(:set_status).with("aha!") 
    @obs.after_save 
    end 
end 

Si vous voulez vraiment vraiment tester le modèle couplé et la classe d'observation, vous pouvez le faire comme ceci:

describe Person do 
    it "should register a status change with the person observer turned on" do 
    Person.with_observers(:person_observer) do 
     lambda { @p = Person.new; @p.save }.should change(@p, :status).to("aha!) 
    end 
    end 
end 

99% des le temps, je préfèrerais spec tester avec les observateurs éteints. C'est juste plus facile de cette façon.

+0

Si vous vouloir tester les observateurs puis un modèle que j'utilise est 'describe PersonOb serveur { around (: each) {| spec | Person.with_observers (: person_observer) {spec.run}}} 'Ceci permet à l'observateur de tous les tests dans le bloc de description de PersonObserver. – roo

+0

Cette réponse est formulée comme une réponse, mais ce n'est pas clair à quoi. Il ne répond certainement pas directement à la question ... –

15

Disclaimer: Je ne l'ai jamais réellement fait cela sur un site de production, mais il semble que d'une manière raisonnable serait d'utiliser des objets fantaisie, should_receive et amis, et appeler des méthodes sur l'observateur directement

Compte tenu de ce qui suit modèle et observateur:

class Person < ActiveRecord::Base 
    def set_status(new_status) 
    # do whatever 
    end 
end 

class PersonObserver < ActiveRecord::Observer 
    def after_save(person) 
    person.set_status("aha!") 
    end 
end 

Je voudrais écrire une spécification comme celui-ci (je l'ai couru, et il passe)

describe PersonObserver do 
    before :each do 
    @person = stub_model(Person) 
    @observer = PersonObserver.instance 
    end 

    it "should invoke after_save on the observed object" do 
    @person.should_receive(:set_status).with("aha!") 
    @observer.after_save(@person) 
    end 
end 
+0

Nous avons suivi cette approche et il fonctionne à merveille –

4

no_peeping_toms est maintenant un petit bijou et se trouve ici: https://github.com/patmaddox/no-peeping-toms

+0

À partir de Rails 3.1 il existe une méthode de désactivation pour les observateurs. –

+0

+1 no_peeping_toms –

2

Si vous voulez vérifier que l'observateur observe le bon modèle et reçoit la notification comme prévu, voici un exemple en utilisant RR.

your_model.rb:

class YourModel < ActiveRecord::Base 
    ... 
end 

your_model_observer.rb:

class YourModelObserver < ActiveRecord::Observer 
    def after_create 
     ... 
    end 

    def custom_notification 
     ... 
    end 
end 

your_model_observer_spec.rb:

before do 
    @observer = YourModelObserver.instance 
    @model = YourModel.new 
end 

it "acts on the after_create notification" 
    mock(@observer).after_create(@model) 
    @model.save! 
end 

it "acts on the custom notification" 
    mock(@observer).custom_notification(@model) 
    @model.send(:notify, :custom_notification) 
end