Is there a way to run `let` once per context?

hello,

I have a monolithic test that looks like this:

======
describe Model do
describe “#method” do
let!(:expensive) { … }

it "should do a lot of things" do
  expensive.should be_foo
  expensive.should be_bar
  expensive.should be_baz
end

end
end

I would like to refactor the large spec into smaller ones:

======
it “should be foo” { expensive.should be_foo }
it “should be bar” { expensive.should be_bar }
it “should be baz” { expensive.should be_baz }

However, doing this slows down the tests a bit, because the
expensive method requires hitting the database.

Suppose that I can guarantee that none of the tests or the methods
they call will be modifying the object returned by the expensive
method. Is there a way to tell RSpec to memoize the expensive result
across the context, and not just across an individual test run?

Alternatively, should I just use before(:all) for this?

John F.
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: User John Feminella - Stack Overflow

On Jun 24, 2011, at 7:17 AM, John F. wrote:

 expensive.should be_foo

it “should be foo” { expensive.should be_foo }
across the context, and not just across an individual test run?

Alternatively, should I just use before(:all) for this?

Just use before(:all). That’s what it is designed for, and it tells you
that you have stuff to clean up in an after(:all) hook.

HTH,
David

Alternatively, should I just use before(:all) for this?

Just use before(:all). That’s what it is designed for, and it tells you that you
have stuff to clean up in an after(:all) hook.

I was about to take your advice, when I discovered another problem:

======
RSpec.configure do |config|
config.mock_with :rspec

config.before(:each, :geocoding => nil) do
Geocoding.stub(:client).and_return(Geocoding::StubGeoClient.new)
end
end

Since the test-specific before-all hook runs first before the global
before-each hook, that means that the expensive operation now hits
an external geocoding service, which makes it take even longer. It
also makes tests fail because WebMock is running, and it prohibits
network connections you don’t explicitly specify.

The fact that this is becoming obnoxious suggests that something else
is probably bad (e.g. maybe the expensive model should be changed to
be less expensive to create). I think I’ll do that instead of trying
to go down this road further. Thanks for your help, David.

~ jf

On Jun 24, 2011, at 8:08 AM, John F. wrote:

config.before(:each, :geocoding => nil) do
Geocoding.stub(:client).and_return(Geocoding::StubGeoClient.new)
end
end

Since the test-specific before-all hook runs first before the global
before-each hook, that means that the expensive operation now hits
an external geocoding service, which makes it take even longer. It
also makes tests fail because WebMock is running, and it prohibits
network connections you don’t explicitly specify.

You can resolve this using a shared group:

http://relishapp.com/rspec/rspec-core/v/2-6/dir/example-groups/shared-examples
http://relishapp.com/rspec/rspec-core/v/2-6/dir/example-groups/shared-context

The fact that this is becoming obnoxious suggests that something else
is probably bad (e.g. maybe the expensive model should be changed to
be less expensive to create).

That’s definitely worth investigating :slight_smile:

I think I’ll do that instead of trying
to go down this road further. Thanks for your help, David.

y/w

Cheers,
David

end

Just use before(:all). That’s what it is designed for, and it tells you that
you have stuff to clean up in an after(:all) hook.


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

Cheers,
David