rails rspec before all vs before each
Asked Answered
D

4

93

contest_entry_spec.rb

    require 'spec_helper'

    describe ContestEntry do

      before(:all) do
        @admission=Factory(:project_admission)
        @project=Factory(:project_started, :project_type => @admission.project_type)
        @creative=Factory(:approved_creative, :creative_category => @admission.creative_category)
        @contest_entry=Factory(:contest_entry, :design_file_name => 'bla bla bla', :owner => @creative, :project => @project)
      end

      context 'non-specific tests' do
        subject { @contest_entry }
        it { should belong_to(:owner).class_name('User') }
        it { should belong_to(:project) }
        it { should have_many(:entry_comments) }

        it { should validate_presence_of(:owner) }
        it { should validate_presence_of(:project) }
        it { should validate_presence_of(:entry_no) }
        it { should validate_presence_of(:title) }

      end
end

When I run these tests everything is okey but if I change before(:all) to before(:each) every test will be failed.I don't know why it happens?

This is the error

 Failure/Error: @contest_entry=Factory(:contest_entry, :design_file_name => 'bla bla bla', :owner => @creative, :project => @project)
     ActiveRecord::RecordInvalid:
       Validation Failed: User is not allowed for this type of project
Duello answered 17/5, 2013 at 20:6 Comment(0)
P
146

before(:all) runs the block one time before all of the examples are run.

before(:each) runs the block one time before each of your specs in the file

before(:all) sets the instance variables @admission, @project, @creative, @contest_entry one time before all of the it blocks are run.

However, :before(:each) resets the instance variables in the before block every time an it block is run.

Its a subtle distinction but important

again,

before(:all)
#before block is run
it { should belong_to(:owner).class_name('User') }
it { should belong_to(:project) }
it { should have_many(:entry_comments) }

it { should validate_presence_of(:owner) }
it { should validate_presence_of(:project) }
it { should validate_presence_of(:entry_no) }
it { should validate_presence_of(:title) }

before(:each)
# before block
it { should belong_to(:owner).class_name('User') }
# before block
it { should belong_to(:project) }
# before block
it { should have_many(:entry_comments) }
# before block

# before block
it { should validate_presence_of(:owner) }
# before block
it { should validate_presence_of(:project) }
# before block
it { should validate_presence_of(:entry_no) }
# before block
it { should validate_presence_of(:title) }
Polash answered 17/5, 2013 at 20:30 Comment(1)
do you know why it failed though with before(:each)Chignon
A
55

An important detail of before :all is that it's not DB transactional. I.e, anything within the before :all persists to the DB and you must manually tear down in the after :all method.

Implications mean that after test suites have completed, changes are not reverted ready for later tests. This can result in complicated bugs and issues with cross contamination of data. I.e, If an exception is thrown the after :all callback is not called.

However, before: each is DB transaction.

A quick test to demonstrate:

1. Truncate your appropriate DB table then try this,

  before :all do
    @user = Fabricate(:user, name: 'Yolo')
  end

2. Observe database afterwards the model remains persisted!

after :all is required. However, if an exception occurs in your test this callback will not occur as the flow was interrupted. The database will be left in an unknown state which can be especially problematic with CI/CD environments and automated testing.

3. now try this,

  before :each do
    @user = Fabricate(:user, name: 'Yolo')
  end

4. Now the database remains devoid of data after the test suite is complete. Far better and leaves us with a consistent state after tests run.

In short, before :each, is probably what you want. Your tests will run slightly slower, but it's worth the expense.

Detail here: https://relishapp.com/rspec/rspec-rails/docs/transactions See: Data created in before(:all) are not rolled back

Hope that helps another weary traveller.

Autotrophic answered 11/6, 2016 at 8:23 Comment(2)
This tips save my day! Just write before to before(:each)Manhood
Note that if you're using the database_cleaner gem, this will work similarly, but it depends on how you've configured it in spec/support/database_cleaner.rb. Check to see if your calls to DatabaseCleaner.start happen before or after a before(:all) block. DatabaseCleaner.start marks the start of your transaction.Jurat
S
5

before(:all), which ensures that the sample users are created once, before all the tests in the block. This is an optimization for speed.

Simmers answered 22/7, 2014 at 6:23 Comment(0)
C
3

One thing to note is by default before use before(:each), also in before(:all) the controller instance is not set so the controller methods like request not used.

Celaeno answered 4/4, 2018 at 7:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.