RSpec chain expect conditions with `and`
The and
method was introduced to RSpec V2 and has been one of my favourite changes to the RSpec API. That change helped me make test code readable and avoid what I would call an "expect pyramid".
subject { described_class.perform(merchant, params) }
it 'authorizes a new transaction' do
expect { subject }
.to change(organization.donations, :count).by 1
.and change(organization.authorisations, :count).by 1
.and change(ActionMailer::Base.deliveries, :count).by 1
end
This is a significant improvement over what it used to be with Ruby 2.
it 'authorizes a new transaction' do
expect do
expect do
expect do
subject
end.to change(organization.donations, :count).by 1
end.to change(organization.authorizations, :count).by 1
end.to change(ActionMailer::Base.deliveries, :count).by 1
end
The above example doesn't look bad, but I'd have to test changes of almost 8 models at once. That made the test code look taller than the pyramids at Giza.
The gotcha
The problem with this approach is that the chaining happens inside a to
. If we need to assert that something does not change in the same chain, we will have to define a negate matcher.
RSpec::Matchers.define_negated_matcher :not_change, :change
Now you have an note_change
matcher. Its not ideal, but it helps keep the test code clean.
it 're-authorizes the transaction' do
expect { subject }
.to not_change(organization.donations, :count)
.and change(organization.authorisations, :count).by 1
.and change(ActionMailer::Base.deliveries, :count).by 1
end
About the Author
Ziyan Junaideen -
Ziyan is an expert Ruby on Rails web developer with 8 years of experience specializing in SaaS applications. He spends his free time he writes blogs, drawing on his iPad, shoots photos.