Integrating Spies into TDD with Ruby and RSpec

Introduction and Context Setting

Welcome to our third lesson on Test-Driven Development (TDD) with Test Doubles. In this lesson, we focus on Spies, an essential type of test double used to observe interactions with your code's dependencies during testing. By now, you've already been introduced to Dummies and Stubs in previous lessons, which allow you to manage dependencies via test doubles effectively.

Our goal here is to seamlessly integrate Spies into the TDD Red-Green-Refactor cycle: writing tests (Red), creating minimal implementations (Green), and refactoring for better quality without altering behavior. Let's dive into understanding and using Spies within this framework using Ruby's RSpec.

Deep Dive into Spies in Ruby

Spies are a powerful feature available through Ruby's RSpec framework. They allow you to observe and record how methods in your application are used without altering their behavior. In TDD, Spies help verify that methods are called when and how you expect them to be, which is crucial for writing reliable tests.

Spies can check:

  • If a method was called
  • How many times it was called
  • With what arguments it was called

They align perfectly with the Red-Green-Refactor cycle:

  • Red: Write a failing test to ensure your code's behavior is verified.
  • Green: Implement only enough code for the tests to pass.
  • Refactor: Clean up the tests and the implementation for better software design.

Let's explore how to set up our environment with RSpec to utilize this powerful tool.

Example: Implementing the First Spy

Let's consider a Notification Service example where we aim to ensure notifications are sent with appropriate priorities. We will begin by implementing a Spy on the send method of the RealNotificationSender. This allows you to use an actual dependency within your test and verify how it was called.

Here's an example test file: spec/notification_service_spec.rb.

Ruby
require 'rspec'

RSpec.describe NotificationService do
  let(:notification_sender) { RealNotificationSender.new }
  let(:notification_service) { NotificationService.new(notification_sender) }

  before do
    allow(notification_sender).to receive(:send).and_call_original
  end

  # Additional tests will go here
end
  • We set up our testing environment using RSpec.
  • We use allow(...).to receive to create a spy on the send method of RealNotificationSender.
  • This Spy will help us verify interactions with the send method.

Next, we insert failing tests to see our Spies in action. Remember that writing failing tests is our "Red" step in TDD.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal