DEV Community

Vasily Polovnyov
Vasily Polovnyov

Posted on

RSpec: one _logical_ expectation per example

Expectation (or verification) is the phase of the test in which we check the actual test result against the correct test result:

expect(post.slug).not_to be_nil
expect(account.admins).to match_array(admins)
expect(comment_notification).to have_received(:deliver)
expect { post.destroy }.to change(Post, :count).by(-1)
Enter fullscreen mode Exit fullscreen mode

Test, which has a lot of expectations, is hard to read and maintain: to find the failing test will have to look for the according line number and read the neighboring. That's why it is better to use one expectation per example.

But there are situations where one logical check is expressed by several physical ones. For example, to check that the color in RGB is blue, we check the red, green and blue component of the color:

describe "#color" do
  it "is blue" do
    expect(color.R).to be < 0.2
    expect(color.G).to be < 0.2
    expect(color.B).to be > 0.8
  end
end
Enter fullscreen mode Exit fullscreen mode

In that case it's better to combine expectations into one with have_attributes:

describe "#color" do
  it "is blue" do
    expect(color).to have_attributes(
      R: (a_value < 0.2),
      G: (a_value < 0.2),
      B: (a_value < 0.8),
    )
  end
end
Enter fullscreen mode Exit fullscreen mode

And if you want to check several related side effects, it is better to combine them with and:

it "activates subscription" do
  expect { subscription.activate! }
    .to change { subscription.reload.active? }.to(true)
    .and change { subscription.reload.valid_until }.to(1.month.from_now)
end
Enter fullscreen mode Exit fullscreen mode

See also:

Top comments (0)