2009-10-02 7 views
1

La spécification suivante fonctionne mais je sais que cela ne devrait pas être comme ça. J'ai du mal à faire tourner ma tête autour de rspec, en particulier les mocks et les stubs.Comment dois-je spécifier ce

C'est le code du modèle

class RecipeFermentable < ActiveRecord::Base 
    belongs_to :recipe 
    belongs_to :product 

    def set_attributes() 
    attrs = product.product_attributes 
    self.ppg = attrs.find_by_name(:ppg.to_s).value 
    self.ecb = attrs.find_by_name(:ecb.to_s).value 
    end 
end 

Et c'est la spécification que j'ai écrit

it "should set the attributes from the product" do 
    @product_attribute1 = mock_model(ProductAttribute, :name => :ppg, :value => 40) 
    @product_attribute2 = mock_model(ProductAttribute, :name => :ecb, :value => 1) 

    @product = Product.new 
    @product.product_attributes << @product_attribute1 
    @product.product_attributes << @product_attribute2 
    @recipe_fermentable = RecipeFermentable.new 
    @recipe_fermentable.product.should_receive(:product_attributes).and_return(@product_attributes) 
    @product_attributes.stub(:find_by_name).with(:ppg.to_s).and_return(@product_attribute1) 
    @product_attributes.stub(:find_by_name).with(:ecb.to_s).and_return(@product_attribute2) 

    @recipe_fermentable.set_attributes 

    @recipe_fermentable.ppg.should eql(40) 
    @recipe_fermentable.ecb.should eql(1) 
end 

Pour commencer mon spec est beaucoup plus grande que ma méthode, et je me sers un vrai produit . Quelques conseils sur la façon d'écrire une bonne spécification pour cela serait vraiment utile. Aussi, si quelqu'un connaît une bonne ressource pour apprendre rspec en utilisant des mock et des stubs, s'il vous plaît pourriez-vous ajouter des liens.

Merci

Répondre

1

Je changerais un ou deux choses:

  • Une grande partie du code dans votre it est tout simplement fournir un contexte, il devrait donc être dans votre avant (: each) bloc.
  • Vous définissez une attente de message, mais cela ne semble pas vraiment être le cas. Je pense que l'attente devrait être remplacée par un stub à la place. Un autre test pourrait être it 'should call product_attributes', où vous testeriez en fait cette attente - je ne vous recommande pas de le faire puisque vous testeriez la mise en œuvre et non le comportement, mais seulement le point.
  • Vous renvoyez @product_attributes dans l'attente de ce message et vous l'utilisez juste après pour remplacer les appels find_by_name. Cependant, vous n'avez jamais défini @product_attributes. Je suppose que cela devrait être un objet fictif, et je ne suis pas sûr de ce que c'est vraiment dans ce contexte. Peut-être que c'est nul, et vous êtes en train de faire quelques méthodes.

Avec ces deux changements, voici où nous en sommes:

before(:each) do 
    @product    = mock_model(Product) 
    @product_attribute_ppg = mock_model(ProductAttribute, :name => :ppg, :value => 40) 
    @product_attribute_ecb = mock_model(ProductAttribute, :name => :ecb, :value => 1) 
    @product_attributes  = mock('product_attributes') 
    @product_attributes.stub!(:find_by_name).with(:ppg.to_s).and_return(@product_attribute_ppg) 
    @product_attributes.stub!(:find_by_name).with(:ecb.to_s).and_return(@product_attribute_ecb) 
    @product.stub!(:product_attributes).and_return(@product_attributes) 

    @recipe_fermentable = RecipeFermentable.new 
    @recipe_fermentable.stub!(:product).and_return(@product) 
end 

it 'should set the attributes from the product' do 
    @recipe_fermentable.set_attributes 
    @recipe_fermentable.ppg.should eql(40) 
    @recipe_fermentable.ecb.should eql(1) 
end 

Avec tout cela tout le chemin, je ne suis pas d'accord complètement avec votre approche ici. Je pense que vous répétez les données et vous éloignez de la normalisation DB. À moins qu'il ya une vraie raison pour cela (peut-être que votre chemin avant et pour des raisons de performance vous deviez le faire), je suggère ce qui suit à la place:

class RecipeFermentable < ActiveRecord::Base 
    def ppg 
    #rescue nil here so that if attributes is nil, or find_by_name('ppg') is nil, things don't blow up 
    product.attributes.find_by_name('ppg').value rescue nil 
    end 

    #other 
end 

Quelques ressources pour les tests:

+0

Merci, cela fait beaucoup de sens. La raison pour laquelle je duplique des données est due au fait que les attributs du produit peuvent changer avec le temps mais que les attributs de la recette doivent rester les mêmes. Sauf si l'utilisateur veut les changer. – Damian

+0

Super. Absolument logique de stocker les attributs de la façon dont vous l'avez fait alors ... – hgmnz

Questions connexes