Today I Learned

hashrocket A Hashrocket project

Nested Expectations in RSpec

Today I learned you can write nested expectations in rspec. I find this is approach useful with writing request-style tests where I want to ensure the request returned successfully and also ensure the expected effects happened.

it "creates the post" do
  expect {
    expect(request.response_code).to eq(200)
  }.to change { Post.count }.by(1)
end

If either the response is not 200, or the Post count doesn't change, then the test fails.

There is a gotcha that I run into when I build these tests iteratively - first the inner expectation on it's own, wrap it in the outer block, and then add the outer matcher. If you wrap the inner request in an expect block, but don't have any assertions on that block, it will always pass - because we're not matching against anything.

RSpec.describe do
  # ❌ - as expected
  it do
    expect(true).to eq(false)
  end

  # ❌ - as expected, outer expectation passes but inner fails, so test fails
  it do
    expect {
      expect(true).to eq(false)
    }.to not_change { 0 }
  end

  # ✅ - :-? watch out for this one, even though the inner expectation fails, test passes
  it do
    expect {
      expect(true).to eq(false)
    }
  end
end
See More #ruby TILs
Looking for help? Each developer at Hashrocket has years of experience working with Ruby applications of all types and sizes. We're an active presence at Ruby conferences, have written some of the most popular gems, and have worked on many of the web's Ruby on Rails success stories. Contact us today to talk about your Ruby project.