Skip to content

puyo/rspec-subject_call

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RSpec Subject Call

This gem permits succinct one liner syntax in situations that currently require more than one line of code. It's for impatient people who use RSpec and like saving keystrokes.

The Situation

There are different types of methods I sometimes need to test. [1]

A query performs a calculation and returns a value, like sum.

A command does one thing. Often it changes one piece of state. e.g. x= or print_document.

A method may also have a number of side effects which you may wish to make assertions about, such as caching, keeping counts or raising exceptions.

To illustrate, here is a class that has all these types of methods.

module ReadmeExample
  class A
    attr :x

    def initialize(b: nil)
      @x = 0
      @b = b
    end

    def query
      1
    end

    def command
      @x = 1
    end

    def query_command
      @x = 1
      1
    end

    def query_command_side_effect
      @b.command
      @x = 1
      1
    end
  end

  class B
    def command
    end
  end
end

Note that query_command_side_effect does ALL the things. So that is a good method to try testing elegantly. If we can test that elegantly, we can test anything elegantly. Right?

With vanilla RSpec, I might test this method like this:

describe "ReadmeExample::A, vanilla" do
  let(:a) { ReadmeExample::A.new(b: b) }
  let(:b) { double('b').as_null_object }

  describe '#query_command_side_effect' do
    it 'is expected to return 1' do
      expect(a.query_command_side_effect).to eq(1)
    end

    it 'is expected to update x' do
      expect { a.query_command_side_effect }.to change(a, :x)
    end

    it 'is expected to call command on b' do
      expect(b).to receive(:command).once
      a.query_command_side_effect
    end
  end
end

But I feel as though this is more typing that I would like and could be DRYed up. For example, a.query_command_side_effect is repeated three times.

The Subject Call Solution

This is how I would like to test this tricky method:

require 'rspec/subject_call'

describe "ReadmeExample::A, subject_call style" do
  let(:a) { ReadmeExample::A.new(b: b) }
  let(:b) { double('b').as_null_object }

  describe '#query_command_side_effect' do
    subject { a.query_command_side_effect }
    it { is_expected.to eq(1) }
    call { is_expected.to change(a, :x) }
    call { is_expected.to meet_expectations { expect(b).to receive(:command) } }
  end
end

In the above example, it is short for the return value of the method under test, and call is a lambda containing the subject, suitable for use with change and raise_error and any other matcher that works with lambdas, already packaged up for you and ready to use.

With this gem, the above example passes.

Installation

gem install rspec-subject_call

If you prefer, copy and paste this into your Gemfile and run bundle:

gem 'rspec-subject_call'

Later that day, ensure this line is included somehow before your spec:

require 'rspec/subject_call'

Now you can use your new powers for good or for awesome.

About

Lets you use the subject in convenient ways with matchers like "change"

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages