Skip to content

Instantly share code, notes, and snippets.

@puyo
Last active December 20, 2015 12:19
Show Gist options
  • Save puyo/6130127 to your computer and use it in GitHub Desktop.
Save puyo/6130127 to your computer and use it in GitHub Desktop.
One liner expect..to..change One liner should_receives One liner to rule them all and in the darkness bind them
module CallingIt
module ExampleGroupClassMethods
def subject(name=nil, &block)
# Add extra methods that allow calling the block that was passed, in the
# example's lexical scope.
if block
define_method(:calling_it) do
lambda { instance_eval(&block) }
end
alias_method(:subject_call, :calling_it)
end
# Now proceed to do the things subject normally does.
super()
end
def calling(method_name, &block)
describe(method_name) do
let(:__its_subject) do
method_chain = method_name.to_s.split('.')
lambda do
method_chain.inject(subject) do |inner_subject, attr|
inner_subject.send(attr)
end
end
end
def should(matcher=nil, message=nil)
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(__its_subject, matcher, message)
end
def should_not(matcher=nil, message=nil)
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(__its_subject, matcher, message)
end
example(&block)
end
end
def calling_it(desc=nil, *args, &block)
# Create a new example, where the subject is set to the subject block,
# as opposed to its return value.
example do
self.class.class_eval do
define_method(:subject) do
if defined?(@_subject_calling_it)
@_subject_calling_it
else
@_subject_calling_it = calling_it
end
end
end
instance_eval(&block)
end
end
end
end
module RSpec
module Core
class ExampleGroup
extend CallingIt::ExampleGroupClassMethods
def subject_call
lambda { subject }
end
end
end
end
class A
attr_reader :counter
def initialize(args = {})
@b = args[:b]
@counter = 0
end
def query
1
end
def command
@b.command
end
def query_command
@b.command
1
end
def query_command_side_effect
@b.command
@counter += 1
1
end
end
class B
def command
end
end
# ----------------------------------------------------------------------
require_relative 'calling_it'
require_relative 'meet_expectations_matcher'
describe A do
let(:a) { A.new(b: b) }
let(:b) { double('b').as_null_object }
describe '#query' do
it 'should return 1' do
a.query.should == 1
end
end
describe '#command' do
it 'should call command on b' do
b.should_receive(:command).once
a.command
end
end
describe '#query_command' do
it 'should return 1' do
a.query_command.should == 1
end
it 'should call command on b' do
b.should_receive(:command).once
a.query_command
end
end
describe '#query_command_side_effect - vanilla best practice' do
it 'should return 1' do
a.query_command_side_effect.should == 1
end
it 'should call command on b' do
b.should_receive(:command).once
a.query_command_side_effect
end
it 'should increment counter' do
expect { a.query_command_side_effect }.to change(a, :counter).by(1)
end
end
describe '#query_command_side_effect - calling_it with custom doc strings' do
subject { a.query_command_side_effect }
it 'should return 1' do
subject.should == 1
end
it 'should call b.command' do
subject_call.should meet_expectations { b.should_receive(:command) }
end
it 'should increment counter by 1' do
subject_call { should change(a, :counter).by(1) }
end
end
describe '#query_command_side_effect - calling_it with one liners' do
subject { a.query_command_side_effect }
it { should == 1 }
calling_it { should change(a, :counter).by(1) }
calling_it { should meet_expectations { b.should_receive(:command) } }
end
describe '#query_command_side_effect - calling_it its edition' do
subject { a }
its(:query_command_side_effect) { should == 1 }
calling(:query_command_side_effect) { should change(a, :counter).by(1) }
calling(:query_command_side_effect) { should meet_expectations { b.should_receive(:command) } }
end
describe '#query_command_side_effect - expect syntax' do
subject { a.query_command_side_effect }
it 'should return 1' do
expect(subject).to eq(1)
end
it 'should call b.command' do
expect(subject_call).to meet_expectations { b.should_receive(:command) }
end
it 'should increment counter by 1' do
expect(subject_call).to change(a, :counter).by(1)
end
end
end
class MeetExpectations
def initialize(&block)
@should_receives = block
end
def matches?(subject)
@should_receives.call # e.g. x.should_receive(:y)
subject.call # execute the subject (assumed to be a Proc)
end
def description
'met expectations'
end
end
module RSpec
module Matchers
def meet_expectations(&block)
MeetExpectations.new(&block)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment