Comments on: Speed up RSpec with set() http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/?utm_source=rss&utm_medium=rss&utm_campaign=speed-up-your-specs-with-set Simple and delicious. Sun, 01 Mar 2015 12:30:00 +0000 hourly 1 By: Philippe Creux http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-7721 Tue, 06 Dec 2011 10:50:40 +0000 http://eggsonbread.com/?p=284#comment-7721 let! creates an object before each expectations
set creates an object once and reload it before each expectation

]]>
By: narshlob http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-7706 Mon, 05 Dec 2011 19:00:55 +0000 http://eggsonbread.com/?p=284#comment-7706 What’s the difference between set() and let!()?

]]>
By: Jaime Bellmyer http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-3105 Sat, 02 Apr 2011 02:53:11 +0000 http://eggsonbread.com/?p=284#comment-3105 hahaha, thanks :)

I’m not opposed to the careful use of before(:all), and I certainly understand the compromises we have to make to keep our test suites running quickly. And you obviously understand that too.

My answer to the build issue has been mocking and stubbing, which I know can clutter the tests at times, and make it more difficult for junior developers to understand/maintain. I think in a perfect world, you wouldn’t deviate from before(:each) and I wouldn’t fake any of my models or associations :) The common culprit is test speed, and so far there isn’t a perfect answer.

I’m to the point where I want to start exploring the idea of serious multicore servers (onsite) dedicated to my company’s testing suites and used by our developers. Our rails shop is growing, so it might even be cost effective – or at least justifiable. Plus, I’ve always wanted a good excuse to own that kind of hardware!

Oh, there’s a gem called dataset you might like. It has the speed of fixtures because it loads the database the same way before tests, but it allows you to have *multiple* sets. So you can have the simplest set of records for a collection of tests, making it easy to remember what everything does. I don’t know if it’s been maintained, I’ll have to check because I could use it myself. And Postgres’ transactions might help, too. So many options, so little time.

Thanks for responding to my comments, it’s great talking to others with this level of dedication to the craft!

]]>
By: Philippe Creux http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-3100 Fri, 01 Apr 2011 22:40:28 +0000 http://eggsonbread.com/?p=284#comment-3100 The pre would have done it. I updated your comment. It looks gorgeous now. :)

]]>
By: Philippe Creux http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-3099 Fri, 01 Apr 2011 22:39:17 +0000 http://eggsonbread.com/?p=284#comment-3099 Thanks for your comment Jaime.

I’m not a big fan of using: subject.should .... I prefere account.should ... by far even if I have to add a let statement. :)

I didn’t have much success with Factory.build. You can’t really use Factory.build whenever your model depends on others (an account must have a user, a balance and an account_email) or you use gems that saves object for you (state_machine does) or you use callbacks (disabling an account will suspend all transactions associated to it). And I want my integration tests to ensure that the changes are stored in the DB anyway. :)

Concerning before(:all) vs before(:each) I totally agree with you. But when your integration specs takes more than 15 minutes to run, you have to speed that up. #set will keep your specs isolated as it reloads the object from the db before every test (and RSpec run tests in isolated db transactions). You can find more examples here: https://github.com/pcreux/rspec-set/blob/master/features/lib/rspec-set.feature

There are some edge cases where the changes are propagated from one test to another though. Using before(:each) fix this kind of issues but we could also setup transactions around contexts. Xavier Shay did that for Postgres: http://rhnh.net/2010/10/06/transactional-before-all-with-rspec-and-datamapper. I’d love to find some time to implement that in rspec-set.

Thanks again for your comment!

]]>
By: Jaime Bellmyer http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-3094 Fri, 01 Apr 2011 22:09:29 +0000 http://eggsonbread.com/?p=284#comment-3094 …by the way, I do indent my code :) I was hoping the code tag would preserve indentations. I created a code snippet with the proper indenting. Thanks again!

]]>
By: Jaime Bellmyer http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-3092 Fri, 01 Apr 2011 22:01:45 +0000 http://eggsonbread.com/?p=284#comment-3092 Hi Philippe! I enjoyed your previous best practices post, but I have to disagree with you here. First, I wanted to ask if you’re aware that you can use the subject freely in your specs? Your first example could be written as:

describe Account do
  subject { Factory(:account) }
 
  it { should be_enabled }
 
  context "when #disable!" do
    before do
      subject.disable!
    end
 
    it { should be_disabled }
  end
end

This eliminates the need for the let block altogether.

Also, the before(:all) block has a bad reputation because your tests are no longer atomic and independent. If you change part of the object in one test, it will be changed for subsequent tests in that group. For instance, the next test you add will start with the account being disabled. This may cause hard-to-find failures. Using before(:each) means being certain you’re not daisy-chaining tests that change the subject along the way.

One speed technique you might want to try is only creating objects on tests that really need them. *Most* tests can be run without the database. Your “it { should be_enabled }” test is one example.

Here’s how my version would look:

describe Account do
  subject { Factory.build(:account) }
 
  it { should be_enabled }
 
  context "#disable!" do
    it "should disable the account" do
      subject.should_not be_disabled
      subject.disable!
      subject.should be_disabled
    end
  end
end

I’m building the account instead of creating, since the first test doesn’t need it anyway. The disable! test will still work correctly, whether it’s saving the model or not. I’m assuming it probably does save, so this approach has removed 2 out of the 3 original database writes, while preserving the clean slate that each test should have when it starts.

I don’t have a before block in the context, because I don’t abstract setup code until there is more than one test requiring the same setup. I did give it its own context, though – I always do that for methods. It makes my tests look more consistent, and the test names are formatted better. And since I agree with your approach that each spec should test as *little* as possible, no method usually stays single-test for long :)

I also ensure that disabled? is returning false before the disable! call, like you expect it to. I always test before and after my method call, to ensure that things have changed correctly, and also catches times where I setup the test wrong to begin with.

Thanks for a great site, I look forward to reading more of your work!

]]>
By: Philippe Creux http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-2310 Tue, 01 Feb 2011 02:12:58 +0000 http://eggsonbread.com/?p=284#comment-2310 You can put set in spec/support/set.rb and require this file in spec/spec_helper.rb

]]>
By: Philippe Creux http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-2309 Tue, 01 Feb 2011 02:12:13 +0000 http://eggsonbread.com/?p=284#comment-2309 This works with RSpec 2.0.

]]>
By: Martin Streicher http://eggsonbread.com/2010/05/25/speed-up-your-specs-with-set/comment-page-1/#comment-2307 Tue, 01 Feb 2011 00:01:13 +0000 http://eggsonbread.com/?p=284#comment-2307 Where do I place set() to use it?

]]>