On Wed, Oct 8, 2008 at 6:01 AM, Jeroen van Dijk
<[EMAIL PROTECTED]> 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
_______________________________________________
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users