DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • All About GPU Threads, Warps, and Wavefronts
  • The Pros and Cons of API-Led Connectivity
  • Your Go-to Guide to Develop Cryptocurrency Blockchain in Node.Js
  • Python Exception Handling: Try, Except, and Finally in Python

Trending

  • Monoliths, REST, and Spring Boot Sidecars: A Real Modernization Playbook
  • Is Big Data Dying?
  • What’s Got Me Interested in OpenTelemetry—And Pursuing Certification
  • Software Delivery at Scale: Centralized Jenkins Pipeline for Optimal Efficiency

RSpec Let vs Before

In this post, we will explore differences between before and let and explain why let is preferred by the Ruby community.

By 
Kristina Garcia Francisco user avatar
Kristina Garcia Francisco
·
Feb. 15, 18 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
39.6K Views

Join the DZone community and get the full member experience.

Join For Free

In RSpec, there are two different ways to write DRY tests, by using before or let. Their purpose is to create variables that are common across tests. In this post, we will explore differences between before and let and explain why let is preferred by the Ruby community.

let

let creates lazily-evaluated local variables. This means that let() is not evaluated until the method that it formed is run for the first time. It DRYs up the spec and makes it more readable.

$count = 0
describe "let" do
  let(:count) { $count += 1 }

  it "stores the value" do
    expect(count).to eq(1)
    expect(count).to eq(1)
  end

  it "is not cached across examples" do
    expect(count).to eq(2)
  end
end

let should not be used for local variables, which have to be saved to the database, as they will not be saved to the database unless they have already been referenced. In this case, you should use let! or before blocks.

Also, never have a let block inside of a before the block, this is what let! is made for!

let!

Unlike let, you can use let! to force the method's invocation before each example. It means that even if you didn't invoke the helper method inside the example it will be invoked before your example runs.

$count = 0
describe "let!" do
  invocation_order = []

  let!(:count) do
    invocation_order << :let!
    $count += 1
  end

  it "calls the helper method in a before hook" do
    invocation_order << :example
    expect(invocation_order).to eq([:let!, :example])
    expect(count).to eq(1)
  end
end

As with let blocks, if multiple let! blocks are defined with the same name, the most recent one will be executed. The core difference is that  let! blocks will be executed multiple times if used like this, whereas the let block will only execute the last time.

before (: each)

The before (: each) block will run before each example, even if the example does not use any of the instance variables defined in the block. This can noticeably slow down the setup of the instance variables.

class User
  def tests
    @tests ||= []
  end
end

describe User do
  before(:each) do
    @user = User.new
  end

  describe "initialized in before(:each)" do
    it "has 0 tests" do
      @user.should have(0).tests
    end

    it "can accept new tests" do
      @user.tests << Object.new
    end

    it "does not share state across examples" do
      @user.should have(0).tests
    end
  end
end

In nearly every situation, it is better to use let over before blocks. Depending on your personal preference you could use before blocks when:

  • There is a reasonable amount of variables.
  • There are variables that don't need to be referenced directly but are required.
  • There are many commands to be executed because its syntax is clearer when many commands are involved.
  • Creating mocks/stubs.

before (: all)

This block is executed only once, before all of the examples in a group. There are certain situations this can cut down on execution and effort.

class User
  def tests
    @tests ||= []
  end
end

describe User do
  before(:all) do
    @user = User.new
  end

  describe "initialized in before(:all)" do
    it "has 0 tests" do
      @user.should have(0).tests
    end

    it "can get accept new tests" do
      @user.tests << Object.new
    end

    it "shares state across examples" do
      @user.should have(1).tests
    end
  end
end

Using runs (: all) in RSpec will cause you lots of trouble unless you know what you are doing! It runs outside of transactions, so the data created here will bleed into other specs.

Conclusion

Let blocks bring more to the table than before blocks. It all depends on what and how you need to make the RSpec tests.

Besides being slower, one of the major problems with before blocks is that spelling errors can lead to bugs and false positives, allowing certain types of tests to pass when they shouldn't.

before(:each) do
    @user = User.find(username: "kolosek")
    @user.logout
end

it "should log the user out" do
    expect(@usr).to be_nil
end

Since @usr was not previously defined, the test will pass, @usr is nil by default. The same test using let would raise NameError because @usr is not defined.

Hope this will help you to better understand the differences between let and before blocks.

Thank you for reading!

Blocks

Published at DZone with permission of Kristina Garcia Francisco, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • All About GPU Threads, Warps, and Wavefronts
  • The Pros and Cons of API-Led Connectivity
  • Your Go-to Guide to Develop Cryptocurrency Blockchain in Node.Js
  • Python Exception Handling: Try, Except, and Finally in Python

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!