Hi all, I''m new to this list and new to RSpec so I have been trying out RSpec the last couple of days and I find it very a natural way of testing. So first of all thanks for providing this framework. Now, I have written some tests for my controllers and models and I saw myself writing similar code, so I began refactoring and came up with the following issue. Here is a simple example of what I first wrote: describe Example do it "should not be valid without attribute1" do Example.new(:attribute2 => "2").should_not be_valid end it "should not be valid without attribute2" do Example.new(:attribute1 => "1").should_not be_valid end end Which I rewrote into another working test: ###### module ExampleSpecHelper def required_valid_attributes {:attribute1 => "1", :attribute2 => "2"} end end describe Example do include ExampleSpecHelper [:attribute1, :attribute2].each do |attribute| before(:all) do @model_with_one_missing_attribute TextMessage.new(required_valid_attributes.except(attribute)) end it "should not be valid without #{attribute}" do @model_with_one_missing_attribute.should_not be_valid end end ##### In this example in you might not see difference in lines of code, but imagine you would have 10 attributes and 10 more tests for each attribute.. Now I rewrote this I was still not satisfied because I thought I would like to use this same approach for several models with different attributes while keeping the logic of this test in one place. Unfortunately, I didn''t come that far because of this: #this works: describe Example do include ExampleSpecHelper required_attributes = [:attribute1, :attribute2] required_attributes.each do |attribute| before(:all) do @model_with_one_missing_attribute TextMessage.new(required_valid_attributes.except(attribute)) end it "should not be valid without #{attribute}" do @model_with_one_missing_attribute.should_not be_valid end end #However this which eventually will be more DRY, but does for some reason not work?! module ExampleSpecHelper ... def required_attributes [:attribute1, :attribute2] end ... end describe Example do include ExampleSpecHelper required_attributes.each do |attribute| before(:all) do @model_with_one_missing_attribute TextMessage.new(required_valid_attributes.except(attribute)) end it "should not be valid without #{attribute}" do @model_with_one_missing_attribute.should_not be_valid end end ############ I don''t understand why it does not work. In the last example required_attributes is nil while the other methods from the helper module such as ''required_valid_attributes'' are available on an even lower level. Why? I hope you understand why I''m trying to refactor it like this. If I can do this I only need to define the required attributes for each model and use it_should_behave_like "an AR model" to keep it DRY. Hope someone can clarify this and that I haven''t done something stupid! Thanks! Cheers, Jeroen -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20081008/ecdebb96/attachment.html>
On Wed, Oct 8, 2008 at 6:01 AM, Jeroen van Dijk <jeroentjevandijk at gmail.com> wrote:> Hi all, > > I''m new to this list and new to RSpec so I have been trying out RSpec the > last couple of days and I find it very a natural way of testing. So first of > all thanks for providing this framework. > > Now, I have written some tests for my controllers and models and I saw > myself writing similar code, so I began refactoring and came up with the > following issue. > > Here is a simple example of what I first wrote: > > describe Example do > > it "should not be valid without attribute1" do > Example.new(:attribute2 => "2").should_not be_valid > end > > it "should not be valid without attribute2" do > Example.new(:attribute1 => "1").should_not be_valid > end > end > > Which I rewrote into another working test: > > ###### > module ExampleSpecHelper > def required_valid_attributes > {:attribute1 => "1", :attribute2 => "2"} > end > end > > describe Example do > include ExampleSpecHelper > [:attribute1, :attribute2].each do |attribute| > before(:all) do > @model_with_one_missing_attribute > TextMessage.new(required_valid_attributes.except(attribute)) > end > > it "should not be valid without #{attribute}" do > @model_with_one_missing_attribute.should_not be_valid > end > end > ##### > > In this example in you might not see difference in lines of code, but > imagine you would have 10 attributes and 10 more tests for each attribute.. > Now I rewrote this I was still not satisfied because I thought I would like > to use this same approach for several models with different attributes > while keeping the logic of this test in one place. Unfortunately, I didn''t > come that far because of this: > > #this works: > describe Example do > include ExampleSpecHelper > required_attributes = [:attribute1, :attribute2] > required_attributes.each do |attribute| > before(:all) do > @model_with_one_missing_attribute > TextMessage.new(required_valid_attributes.except(attribute)) > end > > it "should not be valid without #{attribute}" do > @model_with_one_missing_attribute.should_not be_valid > end > end > > #However this which eventually will be more DRY, but does for some reason > not work?! > module ExampleSpecHelper > ... > def required_attributes > [:attribute1, :attribute2] > end > ... > end > > describe Example do > include ExampleSpecHelper > required_attributes.each do |attribute| > before(:all) do > @model_with_one_missing_attribute > TextMessage.new(required_valid_attributes.except(attribute)) > end > > it "should not be valid without #{attribute}" do > @model_with_one_missing_attribute.should_not be_valid > end > end > > > ############ > > I don''t understand why it does not work. In the last example > required_attributes is nil while the other methods from the helper module > such as ''required_valid_attributes'' are available on an even lower level. > Why? I hope you understand why I''m trying to refactor it like this. If I can > do this I only need to define the required attributes for each model and use > it_should_behave_like "an AR model" to keep it DRY. > > Hope someone can clarify this and that I haven''t done something stupid!There are a couple of problems with this approach. First of all, before(:all) runs only once per group, whereas before(:each) runs repeatedly before(:each) example. This means that whatever gets created before(:all) is shared across examples, whereas what is created in before(:each) is not. Generally speaking, you should avoid before(:all) except for setting up expensive things like database connections. Also, you can write as many different before(:each) or before(:all) blocks and they will all be run, so this: [:attribute1, :attribute2].each do |attribute| before(:all) do @model_with_one_missing_attribute TextMessage.new(required_valid_attributes.except(attribute)) end end is the equivalent of this: before(:all) do @model_with_one_missing_attribute TextMessage.new(required_valid_attributes.except(:attribute1)) end before(:all) do @model_with_one_missing_attribute TextMessage.new(required_valid_attributes.except(:attribute2)) end They get run in order, one time each, resulting in @model_with_one_missing_attribute missing :attribute2 for every time the example gets run. The same problem would happen with before(:each) in this case. What might work here would be something like this: describe "this model" do def valid_attributes {:first => ''valid'', :second => ''valid''} end valid_attributes.keys.each do |attribute| it "should require #{attribute} @model.new(valid_attributes.dup.delete(attribute)).should_not be_valid end end end I''ve seen a number of variations like this posted to this list, so you might want to search the archives for ''model validation''. Also, there are a few plugins that offer ways to do this already: http://github.com/joshknowles/rspec-on-rails-matchers http://github.com/pelargir/rspec_validation_expectations I''m sure there are others. HTH, Cheers, David> Thanks! > > Cheers, > > Jeroen
On 2008-10-08, at 07:01, Jeroen van Dijk wrote:> Hi all, > > I''m new to this list and new to RSpec so I have been trying out > RSpec the last couple of days and I find it very a natural way of > testing. So first of all thanks for providing this framework. > > Now, I have written some tests for my controllers and models and I > saw myself writing similar code, so I began refactoring and came up > with the following issue. > > Here is a simple example of what I first wrote: > > describe Example do > > it "should not be valid without attribute1" do > Example.new(:attribute2 => "2").should_not be_valid > end > > it "should not be valid without attribute2" do > Example.new(:attribute1 => "1").should_not be_valid > end > end > > Which I rewrote into another working test: > > ###### > module ExampleSpecHelper > def required_valid_attributes > {:attribute1 => "1", :attribute2 => "2"} > end > end > > describe Example do > include ExampleSpecHelper > [:attribute1, :attribute2].each do |attribute| > before(:all) do > @model_with_one_missing_attribute = > TextMessage.new(required_valid_attributes.except(attribute)) > end > > it "should not be valid without #{attribute}" do > @model_with_one_missing_attribute.should_not be_valid > end > end > ##### > > In this example in you might not see difference in lines of code, > but imagine you would have 10 attributes and 10 more tests for each > attribute.. Now I rewrote this I was still not satisfied because I > thought I would like to use this same approach for several models > with different attributes while keeping the logic of this test in > one place. Unfortunately, I didn''t come that far because of this: > > #this works: > describe Example do > include ExampleSpecHelper > required_attributes = [:attribute1, :attribute2] > required_attributes.each do |attribute| > before(:all) do > @model_with_one_missing_attribute = > TextMessage.new(required_valid_attributes.except(attribute)) > end > > it "should not be valid without #{attribute}" do > @model_with_one_missing_attribute.should_not be_valid > end > end > > #However this which eventually will be more DRY, but does for some > reason not work?! > module ExampleSpecHelper > ... > def required_attributes > [:attribute1, :attribute2] > end > ... > end > > describe Example do > include ExampleSpecHelper > required_attributes.each do |attribute| > before(:all) do > @model_with_one_missing_attribute = > TextMessage.new(required_valid_attributes.except(attribute)) > end > > it "should not be valid without #{attribute}" do > @model_with_one_missing_attribute.should_not be_valid > end > end > > > ############ > > I don''t understand why it does not work. In the last example > required_attributes is nil while the other methods from the helper > module such as ''required_valid_attributes'' are available on an even > lower level. Why? I hope you understand why I''m trying to refactor > it like this. If I can do this I only need to define the required > attributes for each model and use it_should_behave_like "an AR > model" to keep it DRY. > > Hope someone can clarify this and that I haven''t done something > stupid! Thanks! > > Cheers, > > JeroenHi Jeroen. As I was getting into RSpec and writing specs for my various models, I too found that my code could be a lot DRYer. I ended up writing a module that generates specs for my model attributes. I''ve put it up on GitHub, along with full, lengthy example of how to use it. Have a look: http://github.com/nickhoffman/modelspeccer/ Cheers, Nick