We recently learn about RSpec spies and stubs so it’s now time to create more meaningful specs. When you think about tests you probably think about testing how things work – but how about building the documentation with our tests? You don’t have to do anything extra, you just have to tell a story with your tests and I will show you how.

Story structure

A good story has a nice structure so it’s easy to follow it. You may lose orientation quickly if you would need to jump from one place to another and that’s what you often do when you are looking at the tests.

Have a look at badly designed test:

require 'spec_helper'

describe NameService do
  let(:name_service) { described_class.new(user) }
  let(:user) { instance_double(User, full_name: 'Mike Willson') }
  let(:admin_policy) { instance_double(AdminPolicy, admin?: false) }
  
  before do
    allow(AdminPolicy).to receive(:new).with(user).and_return(admin_policy)
  end
  
  describe '#full_name' do
    subject { name_service.full_name }
    
    it 'returns user full name' do
      expect(subject).to eq(user.full_name)
    end
    
    context 'when a user is an admin' do
      before { allow(admin_policy).to receive(:admin?).and_return(true) }
      
      it 'returns user full name with `ADMIN` prefix' do
        expect(subject).to eq("ADMIN #{user.full_name}")
      end
    end
  end
end

What kind of story does this test tell? Poor story. You start with policy object declaration but you don’t know how it’s used. Let’s just jump to the it tags because you may not want to read the introduction to our story.

Well, it’s a short story, one line. But it takes some time to understand what tested method do without looking at the class code (I didn’t put the class code on purpose although it’s very simple).

Now let’s write a better story:

require 'spec_helper'

describe NameService do
  describe '#full_name' do
    it 'returns user full name' do
      user = instance_double(User, full_name: 'Mike Willson')
      admin_policy = instance_double(AdminPolicy, admin?: false)
      allow(AdminPolicy).to receive(:new).with(user).and_return(admin_policy)
      
      name_service = NameService.new(user)
      full_name = user.full_name
      
      expect(admin_policy).to have_received(:admin?).once
      expect(full_name).to eq(user.full_name)
    end
    
    it 'returns user full name with `ADMIN` prefix if user is an admin' do
      user = instance_double(User, full_name: 'Mike Willson')
      admin_policy = instance_double(AdminPolicy, admin?: true)
      allow(AdminPolicy).to receive(:new).with(user).and_return(admin_policy)
      
      name_service = NameService.new(user)
      full_name = user.full_name
      
      expect(admin_policy).to have_received(:admin?).once
      expect(full_name).to eq("ADMIN #{user.full_name}")
    end
  end
end

Now it’s a better story, much more readable, don’t you think? We have two chapters without the introduction (we can move common code to separated methods to stay DRY). We don’t have to jump, we can easily go from the top to the bottom and each story is separated.

In order to write a good story, you have to stick to the good style of writing. In our example, each story has three parts: setup, exercise, and verification.

Setup

We stubbed classes used in our service, we prepared objects and ensured that we won’t call any external code. You can even say what values will return given class or object used in a tested class. This part is for stubbing things and preparing code for tests.

Exercise

This part is the simplest one. You simply create your class instance and call a method you want to test.

Verification

In the last part you have to verify if everything went ok, the proper result was returned and proper classes were called.

However, you have to remember that such strategy will not be suitable for bigger tests where you are forced to use a lot of let and before blocks in order to keep the test maintainable and readable.

Download free RSpec & TDD ebook

Do you want to earn more or jump to the next level in your company? Do you know that testing skills are one of the most desired skills? There is only first step: start testing and do it right. My ebook can help you. Subscribe to the newsletter to get a free copy of the book.

Leave a Comment

Your email address will not be published. Required fields are marked *