class ChainedMock < RSpec::Mocks::Mock class WrongMethodCalled < StandardError; end def method_missing meth, *args, &block raise WrongMethodCalled.new "expected method #{@next} to be next in the chain, but instead received #{meth}" end def should_receive(meth, opts = {}, &block) @next = meth super end end class Object def should_receive_chain *args if args.size == 1 should_receive(args[0], "missing call to method #{args[0]} in chained expectation") else exp = ChainedMock.new(:chained_expectation) top = args.slice!(0) should_receive(top, "missing call to method #{top} in chained expectation").and_return(exp) if self.class != ChainedMock args.each_with_index do |arg, idx| stub(arg) do raise ChainedMock::WrongMethodCalled.new "expected method #{top} to be first in the chain, but instead received #{arg}" end end end exp.should_receive_chain *args end end end # usage User.should_receive_chain(:registered, :logged_in_once).and_return([user])