Quick tips: Using multiple change matchers with RSpec

It's possible to use multiple RSpec change matchers with a single block of code. The only thing you need is to use the and keyword to link them.

The general form is something like this:

    expect{
        action_that_produces_change
    }.to change(...)
     .and change(...)
     ...
     .and change(...)

Let's see how this can help us using an example.

Imagine you have users with different roles in your application, and there is a special panic event that sends different numbers of notifications depending on the role of the user. You can test for changes in the notification count for every user using the same block:

    admin_user = FactoryBot.create(:user, role: :admin)
    moderator_user = FactoryBot.create(:user, role: :moderator)
    normal_user = FactoryBot.create(:user, role: :normal)

    expect {
        trigger_panic_event
    }.to change(admin_user.notifications, :count).by(3)
     .and change(moderator_user.notifications, :count).by(2)
     .and change(normal_user.notifications, :count).by(1)

This is very useful when writing specs for Rails because sometimes we want to check the effects of a single action on different entities. It helps you create concise specs and prevent code duplication.

I hope this will help you write better specs and software.

Thank you for reading!