Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
120 lines (83 sloc) 2.94 KB

Let It Be!

Let's bring a little bit of magic and introduce a new way to setup a shared test data.

Suppose you have the following setup:

describe BeatleWeightedSearchQuery do
  let!(:paul) { create(:beatle, name: 'Paul') }
  let!(:ringo) { create(:beatle, name: 'Ringo') }
  let!(:george) { create(:beatle, name: 'George') }
  let!(:john) { create(:beatle, name: 'John') }

  specify { expect(subject.call('john')).to contain_exactly(john) }

  # and more examples here
end

We don't need to re-create the Fab Four for every example, do we?

We already have before_all to solve the problem of repeatable data:

describe BeatleWeightedSearchQuery do
  before_all do
    @paul = create(:beatle, name: 'Paul')
    # ...
  end

  specify { expect(subject.call('joh')).to contain_exactly(@john) }

  # ...
end

That technique works pretty good but requires us to use instance variables and define everything at once. Thus it's not easy to refactor existing tests which use let/let! instead.

With let_it_be you can do the following:

describe BeatleWeightedSearchQuery do
  let_it_be(:paul) { create(:beatle, name: 'Paul') }
  let_it_be(:ringo) { create(:beatle, name: 'Ringo') }
  let_it_be(:george) { create(:beatle, name: 'George') }
  let_it_be(:john) { create(:beatle, name: 'John') }

  specify { expect(subject.call('john')).to contain_exactly(john) }

  # and more examples here
end

That's it! Just replace let! with let_it_be. That's equal to the before_all approach but requires less refactoring.

Instructions

In your spec_helper.rb:

require 'test_prof/recipes/rspec/let_it_be'

In your tests:

describe MySuperDryService do
  let_it_be(:user) { create(:user) }

  # ...
end

Caveats

If you modify objects generated within a let_it_be block in your examples, you maybe have to re-initiate them. We have a built-in support for that:

# Use reload: true option to reload user object (assuming it's an instance of ActiveRecord)
# for every example
let_it_be(:user, reload: true) { create(:user) }

# it is almost equal to
before_all { @user = create(:user) }
let(:user) { @user.reload }

# You can also specify refind: true option to hard-reload the record
let_it_be(:user, refind: true) { create(:user) }

# it is almost equal to
before_all { @user = create(:user) }
let(:user) { User.find(@user.id) }

Aliases

@since v0.9.0

Naming is hard. Handling edge cases (the ones described above) is also tricky.

To solve this we provide a way to define let_it_be aliases with the predefined options:

# rails_helper.rb
TestProf::LetItBe.configure do |config|
  # define an alias with `refind: true` by default
  config.alias_to :let_it_be_with_refind, refind: true
end

# then use it in your tests
describe "smth" do
  let_it_be_with_refind(:foo) { Foo.create }

  # refind can still be overridden
  let_it_be_with_refind(:bar, refind: false) { Bar.create }
end
You can’t perform that action at this time.