The Advantages of ‘have_received’ for Setting Message Expectations in RSpec

Examples and Best Practices

Patrick Karsh
3 min readMay 8, 2023
Give your testing robots better commands

RSpec, a popular testing framework for Ruby, provides a powerful and flexible way to write test suites for your applications. One of the many benefits of using RSpec is the ability to set message expectations, ensuring that your code communicates effectively with other components in your system. In this article, we’ll explore the advantages of using ‘have_received’ for setting message expectations in RSpec and walk through practical examples that demonstrate these benefits.

Advantages of ‘have_received’ for Setting Message Expectations in RSpec

Improved Readability and Clarity

The ‘have_received’ syntax leads to more readable and clear test code, as it closely resembles natural language. By leveraging this syntax, it becomes easier for developers to understand the purpose and expected outcome of a test.

Example:

it "sends a welcome email to the user" do
user = User.new(email: "user@example.com")
mailer = double("Mailer")
allow(Mailer).to receive(:send_welcome_email)

user.send_welcome_email(mailer)

expect(mailer).to have_received(:send_welcome_email).with(user)
end

In this example, the use of ‘have_received’ makes the test’s intent easy to comprehend, as it clearly states that we expect the mailer to have received the ‘send_welcome_email’ message with the user as an argument.

Even a human could understand.

Enhanced Test Isolation

Using ‘have_received’ allows for better test isolation, as it focuses on verifying the interaction between objects instead of directly testing the outcome. This prevents tests from becoming too dependent on the implementation details of the code being tested.

Example:

it "notifies an external service when a new user is created" do
user = User.new(email: "user@example.com")
notifier = double("Notifier")
allow(notifier).to receive(:notify)

user.create_and_notify(notifier)

expect(notifier).to have_received(:notify).with(kind_of(User))
end

In this example, we test that the notifier receives the ‘notify’ message with a user object without relying on the actual creation of the user, allowing for better test isolation.

Flexibility in Test Setup

‘have_received’ provides flexibility in how you set up your tests, as it allows you to define the behavior of a test double before or after the message is sent. This is especially useful when you have multiple test doubles or complex scenarios.

Example:

it "sends a daily summary email to all users" do
user1 = User.new(email: "user1@example.com")
user2 = User.new(email: "user2@example.com")
mailer = double("Mailer")
allow(mailer).to receive(:send_daily_summary)

User.send_daily_summaries(mailer)

expect(mailer).to have_received(:send_daily_summary).with(user1).once
expect(mailer).to have_received(:send_daily_summary).with(user2).once
end

In this example, we can set up the expectation for the mailer to receive ‘send_daily_summary’ with different user objects in a single test, showcasing the flexibility provided by ‘have_received’.

It does not take a robot to understand this spec.

Conclusion

The ‘have_received’ syntax in RSpec provides numerous advantages for setting message expectations, including improved readability, enhanced test isolation, and flexibility in test setup. By incorporating this syntax into your test suites, you can ensure that your applications are not only thoroughly tested but also maintainable and easy to understand.

--

--

Patrick Karsh
Patrick Karsh

Written by Patrick Karsh

NYC-based Ruby on Rails and Javascript Engineer leveraging AI to explore Engineering. https://linktr.ee/patrickkarsh

No responses yet