DEV Community

Germán Alberto Gimenez Silva
Germán Alberto Gimenez Silva

Posted on • Originally published at rubystacknews.com on

RSpec Mocks & Doubles: The Secret Sauce for Testing Like a Pro 🧪

February 25, 2025

Let’s face it—writing tests can sometimes feel like trying to solve a Rubik’s Cube blindfolded. You’re juggling dependencies, edge cases, and external systems, all while hoping your code doesn’t break in production. But fear not, because RSpec mocks and test doubles are here to save the day (and your sanity).

In this article, we’ll dive into how you can use RSpec’s mocking tools to write cleaner, faster, and more reliable tests. Whether you’re new to testing or just looking to level up, these tips will help you test like a pro without pulling your hair out.


Need Expert Ruby on Rails Developers to Elevate Your Project?

Fill out our form! >>


Need Expert Ruby on Rails Developers to Elevate Your Project?


What Are Mocks and Test Doubles?

Think of mocks and test doubles as the stunt doubles of the testing world. They step in when you don’t want to deal with the real thing—like that one actor who’s always late to set. In Ruby, these “stunt doubles” let you simulate objects, control their behavior, and verify interactions without relying on the actual implementation.

For example, if your code sends emails or talks to a database, you don’t want your tests spamming people or making unnecessary queries. That’s where mocks come in—they fake the behavior so you can focus on what really matters.


Why Should You Care About Mocks?

  1. Isolation is Key : Mocks let you test your code in isolation. No need to drag in the whole system—just focus on the part you’re working on.
  2. Simulate the Impossible : Want to test how your app handles a server outage? With mocks, you can simulate that scenario without actually crashing anything.
  3. Speed Things Up : Tests that rely on mocks run way faster because they skip time-consuming operations like database calls or API requests.
  4. Keep It Clean : By replacing dependencies with mocks, your tests stay focused and easier to maintain.

The Essentials: Mocks, Stubs, and Spies

1. Test Doubles

A test double is just a stand-in object. You can create one like this:

user = double("User")
Enter fullscreen mode Exit fullscreen mode

This creates a mock object named “User”. From there, you can define how it behaves or what methods it should respond to.


2. Stubs

Stubs are like pre-programmed responses. For example:

allow(user).to receive(:name).and_return("John Doe")
puts user.name # Outputs: "John Doe"
Enter fullscreen mode Exit fullscreen mode

Here, we’re telling the name method to always return “John Doe”, no matter what.


3. Expectations

Expectations are where the magic happens. They let you verify that certain methods are called with the right arguments. For example:

expect(user).to receive(:save).and_return(true)
user.save # This must be called for the test to pass
Enter fullscreen mode Exit fullscreen mode

If save isn’t called during the test, RSpec will throw an error—and you’ll know something’s off.


4. Spies

Spies are like the laid-back cousin of mocks. Instead of setting expectations upfront, they record interactions after the fact. For example:

user = spy("User")
user.save
expect(user).to have_received(:save)
Enter fullscreen mode Exit fullscreen mode

This approach is great when you want to check that something happened without being too strict about the order.


Real-World Example: Sending Emails Without Spamming Your Users

Let’s say you have a NotificationService class that sends emails using a Mailer:

class NotificationService
  def initialize(mailer)
    @mailer = mailer
  end

  def notify_user(email)
    @mailer.deliver(to: email)
  end
end
Enter fullscreen mode Exit fullscreen mode

To test this without actually sending emails, you can use a test double for the Mailer:

describe NotificationService do
  it "sends a notification email" do
    # Create a test double for the Mailer
    mailer = double("Mailer")

    # Set an expectation that the deliver method will be called
    expect(mailer).to receive(:deliver).with(to: "user@example.com")

    # Initialize the service with the mock mailer
    service = NotificationService.new(mailer)

    # Call the method under test
    service.notify_user("user@example.com")
  end
end
Enter fullscreen mode Exit fullscreen mode

In this test:

  • We create a mock Mailer.
  • We set an expectation that the deliver method gets called with the right email address.
  • If everything works as expected, the test passes—and no one gets spammed!

Pro Tips for Using Mocks

  1. Don’t Overdo It : Mocking every little thing can make your tests brittle. Use mocks for external dependencies, but try to keep the rest of your code as close to reality as possible.
  2. Combine with Integration Tests : While mocks are awesome for unit tests, don’t forget to write integration tests that check how everything works together.
  3. Keep It Readable : Name your doubles clearly and document what each mock or stub represents. Future-you will thank you.

Wrapping It Up

RSpec’s mocking tools are like having a Swiss Army knife for testing—they give you the flexibility to handle almost any scenario. Whether you’re simulating edge cases, isolating dependencies, or verifying interactions, mocks and test doubles can help you write tests that are both fast and reliable.

So next time you’re writing tests, don’t shy away from using mocks. They might just become your new favorite tool in the testing toolbox.


Your Turn!

What’s your take on using mocks in testing? Love them? Hate them? Somewhere in between? Drop a comment below—I’d love to hear your thoughts and keep the conversation going. Let’s learn from each other and grow as developers!

Until next time, happy coding! ✌

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

đź‘‹ Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay