On Dec 6, 2010, at 8:12 PM, Andrew Kasper wrote:
> Hi gang.
>
> I''ve come across what I believe to be unexpected behavior for some
of my before :each blocks, and I wonder if anyone can enlighten me as to why
this is happening.
>
> The surprising thing happens when I run a ''before :each''
inside of an each block on a Hash. I have a #before_save hook in my ActiveRecord
model. When I call @sample_model.save from the before block, the model runs a
uniqueness validation. However, when I save from within the example, the save
works fine.
>
> Below is the minimal test case I was able to put together:
>
> class SampleClassMigration < ActiveRecord::Migration
> def self.up
> create_table :sample_classes do |t|
> t.string :sample_field
> t.string :attrib_1
> t.string :attrib_2
> end
> end
> end
>
> class SampleClass < ActiveRecord::Base
> validates_uniqueness_of :sample_field
> before_save :prep
> def prep
> self.sample_field = ''sample_value''
> end
> end
>
> describe SampleClass do
> EXAMPLES = {
> :attrib_1 => ''foo'',
> :attrib_2 => ''bar''
> }
> EXAMPLES.each do |key, value|
> before(:each) do
> @sample = SampleClass.new(key => value)
> #In the passing case, this call to #save
> #is moved into the ''it'' block
> @sample.save
> end
>
> it "key : #{value}\tvalue : #{key.to_s}" do
> #in the other case, I call the spec here
> @sample.should be_valid
> end
> end
> end
>
> It was my expectation that calling an instance method in the before block
would be the same as calling it within the example block. Instead, I get two
different results. When called from within the it block, both examples pass.
When called from within the before block, they both fail:
>
> 1)
> ''SampleClass key : bar value : attrib_2'' FAILED
> Expected #<SampleClass id: 2, sample_field: "sample_value",
attrib_1: "foo", attrib_2: nil> to be valid, but it was not
> Errors: Sample field has already been taken
> ./spec/models/minimal_spec.rb:20:
>
> 2)
> ''SampleClass key : foo value : attrib_1'' FAILED
> Expected #<SampleClass id: 2, sample_field: "sample_value",
attrib_1: "foo", attrib_2: nil> to be valid, but it was not
> Errors: Sample field has already been taken
> ./spec/models/minimal_spec.rb:20:
>
> I''m surprised to see that the example fails, but I''m more
surprised to see that it fails both times. It is apparently the case that the
"before" block is executed on every iteration of the "each"
block (but that the examples themselves are executed later).
>
> 1) Is this a bug?
Nope.
> 2) Is it a known behavior?
Yep. Examples are evaluated _after_ they are _all_ read in and organized.
Because each iteration adds a before(:each) block, the code above has the same
behavior as this:
describe SampleClass do
before(:each) do
@sample = SampleClass.new(:attrib_1 => ''foo'')
@sample.save
end
before(:each) do
@sample = SampleClass.new(:attrib_2 => ''bar'')
@sample.save
end
it "key : foo\tvalue : attrib_1" do
@sample.should be_valid
end
it "key : bar\tvalue : attrib_2" do
@sample.should be_valid
end
end
Seeing it this way, it is clear that there are two before(:each) hooks that both
run before each example, and that the 2nd before(:each) hook results in a
failure each time.
Make sense?
> 3) Is there a "BDD-theoretical" better way to do something like
this (assuming a larger hash of examples, for instance)?
You could create a separate context for each iteration, and each one would have
it''s own before hook:
describe SampleClass do
EXAMPLES = {
:attrib_1 => ''foo'',
:attrib_2 => ''bar''
}
EXAMPLES.each do |key, value|
context "with :#{key} => #{value}" do
before(:each) do
@sample = SampleClass.new(key => value)
#In the passing case, this call to #save
#is moved into the ''it'' block
@sample.save
end
it "key : #{value}\tvalue : #{key.to_s}" do
#in the other case, I call the spec here
@sample.should be_valid
end
end
end
end
In terms theory, the trick here is that when you take short-cuts like this you
bind all the examples together. That tends to work fine until requirements
change such that one of the iterations needs to change in a way that the others
don''t.
HTH,
David
>
> I''m using:
>
> gem ''rails'', ''2.3.5''
> gem ''mysql''
> group :development, :test do
> gem ''database_cleaner''
> gem ''rspec-rails'', ''1.3.2''
> gem ''rspec'', ''1.3.0''
> end
>
> DatabaseCleaner is properly configured, and runs for many other specs.
(Indeed, it even runs correctly between steps, as demonstrated by the case where
the specs pass).
>
> Thanks,
> Andrew Kasper
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
Cheers,
David
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://rubyforge.org/pipermail/rspec-users/attachments/20101206/8c51518f/attachment.html>