Skip to content

Instantly share code, notes, and snippets.

@ichilton
Last active December 29, 2015 12:59
Show Gist options
  • Save ichilton/7674331 to your computer and use it in GitHub Desktop.
Save ichilton/7674331 to your computer and use it in GitHub Desktop.
rspec ordering problem
# This doesn't seem to work because the before(:each) is not called before the inner before(:all) is run...
context "when updating" do
before(:each) { @bible = create(:bible, code: 'OLD_CODE', alias: 'OLD_ALIAS') }
context "when not updating alias" do
before(:all) do
@bible.update_attributes(name: 'Different Name', code: 'UPDATED')
end
it "should have the updated title" do
expect(@bible.name).to eq 'Different Name'
end
it "should have the updated code" do
expect(@bible.code).to eq 'UPDATED'
end
it "should have the old alias" do
expect(@bible.alias).to eq 'OLD_ALIAS'
end
end
context "when updating alias" do
before(:all) do
bible.update_attributes(name: 'Different Name', code: 'UPDATED', alias: 'NEW_ALIAS')
end
it "should have the updated title" do
expect(bible.name).to eq 'Different Name'
end
it "should have the updated code" do
expect(bible.code).to eq 'UPDATED'
end
it "should have the old alias" do
expect(bible.alias).to eq 'NEW_ALIAS'
end
end
context "when updating alias to nil" do
before(:all) do
bible.update_attributes(name: 'Different Name', code: 'UPDATED', alias: nil)
end
it "should have the updated title" do
expect(bible.name).to eq 'Different Name'
end
it "should have the updated code" do
expect(bible.code).to eq 'UPDATED'
end
it "should have the correct alias (same as code)" do
expect(bible.alias).to eq 'UPDATED'
end
end
end
# Changing the before(:each) to let becomes:
context "when updating" do
let(:bible) { create(:bible, code: 'OLD_CODE', alias: 'OLD_ALIAS') }
context "when not updating alias" do
before(:all) do
bible.update_attributes(name: 'Different Name', code: 'UPDATED')
end
it "should have the updated title" do
expect(bible.name).to eq 'Different Name'
end
it "should have the updated code" do
expect(bible.code).to eq 'UPDATED'
end
it "should have the old alias" do
expect(bible.alias).to eq 'OLD_ALIAS'
end
end
context "when updating alias" do
before(:all) do
bible.update_attributes(name: 'Different Name', code: 'UPDATED', alias: 'NEW_ALIAS')
end
it "should have the updated title" do
expect(bible.name).to eq 'Different Name'
end
it "should have the updated code" do
expect(bible.code).to eq 'UPDATED'
end
it "should have the old alias" do
expect(bible.alias).to eq 'NEW_ALIAS'
end
end
context "when updating alias to nil" do
before(:all) do
bible.update_attributes(name: 'Different Name', code: 'UPDATED', alias: nil)
end
it "should have the updated title" do
expect(bible.name).to eq 'Different Name'
end
it "should have the updated code" do
expect(bible.code).to eq 'UPDATED'
end
it "should have the correct alias (same as code)" do
expect(bible.alias).to eq 'UPDATED'
end
end
end
# But this gives:
WARNING: let declaration `bible` accessed in a `before(:all)` hook at:
/vagrant/myproj_v2/spec/models/bible_spec.rb:140:in `block (5 levels) in <top (required)>'
This is deprecated behavior that will not be supported in RSpec 3.
`let` and `subject` declarations are not intended to be called
in a `before(:all)` hook, as they exist to define state that
is reset between each example, while `before(:all)` exists to
define state that is shared across examples in an example group.
@ichilton
Copy link
Author

What's the correct way to do this?

I want to have a record which is created for each context, and each context to have an update that is performed once for that context...

@ichilton
Copy link
Author

I've got it working like this, but it's inefficient because of the creating and destroying for each assertion :( - is there a neater way?

  describe "alias behaviour" do
    context "when alias is provided" do
      before(:all) { @bible = create(:bible, code: 'CODE', alias: 'ALIAS') }

      it "should use the provided alias" do
        expect(@bible.alias).to eq 'ALIAS'
      end

      it "should have the correct code" do
        expect(@bible.code).to eq 'CODE'
      end

      it "should not have the same code and alias" do
        expect(@bible.alias).not_to eq @bible.code
      end
    end

    context "when alias is not provided" do
      before(:all) { @bible = create(:bible, code: 'FRED', alias: nil) }

      it "should have the correct alias" do
        expect(@bible.alias).to eq 'FRED'
      end

      it "should have the same code and alias" do
        expect(@bible.alias).to eq @bible.code
      end
    end

    context "when updating" do
      before(:each) { @bible = create(:bible, code: 'OLD_CODE', alias: 'OLD_ALIAS') }
      after(:each) { @bible.destroy }

      context "when not updating alias" do
        before(:each) { @bible.update_attributes(name: 'Different Name', code: 'UPDATED') }

        it "should have the updated title" do
          expect(@bible.name).to eq 'Different Name'
        end

        it "should have the updated code" do
          expect(@bible.code).to eq 'UPDATED'
        end

        it "should have the old alias" do
          expect(@bible.alias).to eq 'OLD_ALIAS'
        end
      end

      context "when updating alias" do
        before(:each) { @bible.update_attributes(name: 'Different Name', code: 'UPDATED', alias: 'NEW_ALIAS') }

        it "should have the updated title" do
          expect(@bible.name).to eq 'Different Name'
        end

        it "should have the updated code" do
          expect(@bible.code).to eq 'UPDATED'
        end

        it "should have the old alias" do
          expect(@bible.alias).to eq 'NEW_ALIAS'
        end
      end

      context "when updating alias to nil" do
        before(:each) { @bible.update_attributes(name: 'Different Name', code: 'UPDATED', alias: nil) }

        it "should have the updated title" do
          expect(@bible.name).to eq 'Different Name'
        end

        it "should have the updated code" do
          expect(@bible.code).to eq 'UPDATED'
        end

        it "should have the correct alias (same as code)" do
          expect(@bible.alias).to eq 'UPDATED'
        end
      end
    end
  end

@ichilton
Copy link
Author

Not quite as DRY, but I think this is neater....

  describe "alias behaviour" do
    context "when alias is provided" do
      before(:all) { @bible = create(:bible, code: 'CODE', alias: 'ALIAS') }

      it "should use the provided alias" do
        expect(@bible.alias).to eq 'ALIAS'
      end

      it "should have the correct code" do
        expect(@bible.code).to eq 'CODE'
      end

      it "should not have the same code and alias" do
        expect(@bible.alias).not_to eq @bible.code
      end
    end

    context "when alias is not provided" do
      before(:all) { @bible = create(:bible, code: 'FRED', alias: nil) }

      it "should have the correct alias" do
        expect(@bible.alias).to eq 'FRED'
      end

      it "should have the same code and alias" do
        expect(@bible.alias).to eq @bible.code
      end
    end

    context "when updating" do
      context "when not updating alias" do
        before(:all) do
          @bible = create(:bible, code: 'OLD_CODE', alias: 'OLD_ALIAS')
          @bible.update_attributes(name: 'Different Name', code: 'UPDATED')
        end

        after(:all) { @bible.destroy}

        it "should have the updated title" do
          expect(@bible.name).to eq 'Different Name'
        end

        it "should have the updated code" do
          expect(@bible.code).to eq 'UPDATED'
        end

        it "should have the old alias" do
          expect(@bible.alias).to eq 'OLD_ALIAS'
        end
      end

      context "when updating alias" do
        before(:all) do
          @bible = create(:bible, code: 'AN_OLD_CODE', alias: 'AN_OLD_ALIAS')
          @bible.update_attributes(name: 'A Different Name', code: 'AN_UPDATED_CODE', alias: 'NEW_ALIAS')
        end

        it "should have the updated title" do
          expect(@bible.name).to eq 'A Different Name'
        end

        it "should have the updated code" do
          expect(@bible.code).to eq 'AN_UPDATED_CODE'
        end

        it "should have the old alias" do
          expect(@bible.alias).to eq 'NEW_ALIAS'
        end
      end

      context "when updating alias to nil" do
        before(:all) do
          @bible = create(:bible, code: 'ANOTHER_OLD_CODE', alias: 'ANOTHER_OLD_ALIAS')
          @bible.update_attributes(name: 'Another Different Name', code: 'ANOTHER_UPDATED_CODE', alias: nil)
        end

        it "should have the updated title" do
          expect(@bible.name).to eq 'Another Different Name'
        end

        it "should have the updated code" do
          expect(@bible.code).to eq 'ANOTHER_UPDATED_CODE'
        end

        it "should have the correct alias (same as code)" do
          expect(@bible.alias).to eq 'ANOTHER_UPDATED_CODE'
        end
      end
    end
  end

@pootsbook
Copy link

Let me blow your mind:

  describe "alias behaviour" do
  context "with alias" do
    subject { create(:bible, code: 'CODE', alias: 'ALIAS') }

    its(:alias) { should eq 'ALIAS' }
    its(:code)  { should eq 'CODE'  }
    its(:alias) { should_not eq subject.code }
  end

  context "when alias is not provided" do
    subject { create(:bible, code 'FRED', alias: nil) }

    its(:alias) { should eq 'FRED' }
    its(:alias) { should eq subject.code }
  end

  context "when updating" do
    context "when not updating alias" do
      subject { create(:bible, code: 'OLD_CODE', alias: 'OLD_ALIAS') }
      before(:all) { subject.update_attributes(name: 'Different Name', code: 'UPDATED') }
      after(:all)  { subject.destroy}

      its(:name)  { should eq 'Different Name' }
      its(:code)  { should eq 'UPDATED' }
      its(:alias) { should eq 'OLD_ALIAS' }
    end
  end
  
end

@ichilton
Copy link
Author

Thanks Philip.

That does look a lot neater/shorter, but the problem with subject is it works like before(:each) and let in that it runs for each test - that's why i've started doing things like:

  before(:all) { @something = create(:something) }
  subject { @something }

The shorthand its syntax does look better in the specs, but does make the output a bit ambiguous as all you see is the test data, so I tend to use that more for things like be_valid....

Thanks,

Ian

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment