One thing I noticed, and something that always catches me out is the
fact that the 'let’ted object doesn’t get instantiated until it gets
referenced. Therefore,
describe “all” do
let(:foo) { Foo.create! }
it "returns the created object" do
Foo.all.should include(foo)
end
end
… fails, since at the time of calling :all, the ‘foo’ object hasn’t
been referred yet, and hence the block hasn’t executed. “Foo.all” in the
case above returns an empty array, which wouldn’t have been the case
with an instance object created in “before(:each)”.
On Sep 30, 2011, at 3:31 PM, Patrick J. Collins wrote:
In that case each reference to thing returns the same object.
end @foo.bar
I am not seeing any difference…?
There is not, really, other than how the declaration of foo is expressed
and referenced. This evolved out of a common pattern in TDD:
1:
describe “something” do
it “does something” do
thing = Thing.new
thing.do_something.should have_some_outcome
end
end
2:
describe “something” do
it “does something” do
thing = Thing.new
thing.do_something.should have_some_outcome
end
it “does something else” do
thing = Thing.new
thing.do_something_else.should have_some_other_outcome
end
end
Now there is duplication so we can refactor out the declaration of
thing. It takes less work and is less error prone to change it to a let
declaration than to change the references to thing to an instance
variable declared in a before hook.
On Sep 30, 2011, at 4:32 PM, Srushti Ambekallu wrote:
thing.yet_again
end
end
end
it “does something else” do
Foo.all.should include(foo)
end
end
… fails, since at the time of calling :all, the ‘foo’ object hasn’t been
referred yet, and hence the block hasn’t executed. “Foo.all” in the case above
returns an empty array, which wouldn’t have been the case with an instance object
created in “before(:each)”.
Proper usage, sure, but the memoization is only within each example - not
across examples. That way you can do this:
So regarding objects persisting over multiple examples-- I was told
repeatedly
by experienced RSpec peeps to not use before(:all)…
But in a case like:
before(:each) do @user = create_user
create_user_item(:user => @user)
end
it “has an item” do @user.user_item.should_not be_nil
end
it “rocks the house” do @user.user_item.should respond_to(:rocks_the_house)
end
…etc…
It seems like this is an instance where before :all, really would shine
because
it would not require records to be repeatedly created…
Do you guys feel like before(:all) is just bad because of the
possibility of a
method call in one example changing the state and therefore breaking
future
examples and not having it be clear as to why… ?
I don’t know if object creation (including the corresponding database
calls) are really expensive enough that I would worry about optimising
that (unless you’re getting it anyway, without losing anything in the
process, like with ‘let’). Occasionally, for some reason I’ll end up
using a ‘before(:all)’ for one isolated scenario, but inevitably a few
months down the line, I’ll end up wasting a couple of hours on a weird
failing test, until I look closely enough to notice the “:all”.
So, unless I’m doing something that’s prohibitively expensive (I haven’t
found anything that would qualify in any of my projects) I’m not likely
to create anything in a ‘before(:all)’.
So regarding objects persisting over multiple examples-- I was told repeatedly
by experienced RSpec peeps to not use before(:all)
I don’t know if object creation (including the corresponding database calls) are
really expensive enough that I would worry about optimising that (unless you’re
getting it anyway, without losing anything in the process, like with ‘let’).
Occasionally, for some reason I’ll end up using a ‘before(:all)’ for one isolated
scenario, but inevitably a few months down the line, I’ll end up wasting a couple
of hours on a weird failing test, until I look closely enough to notice the
“:all”.
So, unless I’m doing something that’s prohibitively expensive (I haven’t found
anything that would qualify in any of my projects) I’m not likely to create
anything in a ‘before(:all)’.
I’ll second that. It’s better to feel the pain of the multiple database
hits and refactor it out in the code (can you separate more of the logic
and persistence?), than try to optimise the test run by introducing
shared state. In the long run, the former will give you many faster test
runs through better design, where as the latter will give you a few
faster tests now at the risk of - as Srushti points out - stepping on a
landmine later.