Not without a reason before and let blocks were created by RSpec authors but it’s better to avoid them when we don’t really need to use them. Here is why:

  1. You disorder the natural flow of the test by jumping from the test body to the let definition and vice versa.
  2. You slow down your tests as let is transformed into a simple method at the end - memory usage benchmark
  3. Your specs are less readable because you have to jump from one place to another to get the right context
  4. Your specs depend on each other and they should be separated

Let’s consider now an example of tests written with let and before blocks and without:

require 'spec_helper'

describe Users::NameService do
  describe '#name' do
    let(:user) { instance_double(User, posts?: false) }

    it 'returns user name' do
      expect(described_class.new(user).name).to eq(user.name)
    end

    context 'when the user has posts' do
      before { allow(user).to receive(:posts?).and_return(true) }

      it 'returns user name' do
        expect(described_class.new(user).name).to eq("#{user.name} (has posts)")
      end
    end
  end
end

and the same test without before and let:

describe Users::NameService do
  describe '#name' do
    it 'returns user name' do
      user = instance_double(User, posts?: false)

      expect(described_class.new(user).name).to eq(user.name)
    end

    it 'returns user name if the user has posts' do
      user = instance_double(User, posts?: true)

      expect(described_class.new(user).name).to eq("#{user.name} (has posts)")
    end
  end
end

With the second version we have two separated test stories and to understand one of them we don’t have to jump outside of it block. We repeat ourselves a little bit but sometimes readability is more important than using Don't Repeat Yourself (DRY) rule. Our test is also a little bit faster - it’s more visible in larger tests but it’s always good to make spec as fast as we can without hurting logic and readability. We also get rid of one stub - profit!

Do you have an example where there is no way to get rid of before or let block? Share it with us in the comments section.

Want to receive useful tips, information about new Ruby gems and articles on a daily basis? Make sure you follow me and say hello!

Update 10/02/2018

I updated the post with a link to the simple benchmark results, I should do this before writing this post. You can view them here. Also many people didn't understand my point of view properly - I didn't said to always avoid let and before - there are cases when using before and let gives us more power and speed up our specs - especially in complex cases. However, I always try to write a simple code and found such approach more useful.

Want to become a better Rails developer?
Download for free the Introduction Rails patterns book and dive into the world of refactoring and easy-testable Ruby code today.
Join over 1,000 developers already subscribed to my newsletter and download the book. You can unsubscribe anytime:

Subscribe and get the book!