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.
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.
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.
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.