require 'spec_helper' module Spec module Mocks describe Mock do treats_method_missing_as_private :subject => Mock.new, :noop => false before(:each) do @mock = mock("test mock") end after(:each) do @mock.rspec_reset end describe "deprecated #stub_everything method" do before(:each) do Kernel.stub!(:warn) end it "creates a mock that behaves as a null object" do stub_everything.should be_null_object end it "provides deprecation warning" do Kernel.should_receive(:warn).with(/DEPRECATION: stub_everything.* is deprecated./) stub_everything end end it "should report line number of expectation of unreceived message" do expected_error_line = __LINE__; @mock.should_receive(:wont_happen).with("x", 3) begin @mock.rspec_verify violated rescue MockExpectationError => e # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line e.backtrace[0].should match(/#{File.basename(__FILE__)}:#{expected_error_line}/) end end it "should report line number of expectation of unreceived message after #should_receive after similar stub" do @mock.stub!(:wont_happen) expected_error_line = __LINE__; @mock.should_receive(:wont_happen).with("x", 3) begin @mock.rspec_verify violated rescue MockExpectationError => e # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line e.backtrace[0].should match(/#{File.basename(__FILE__)}:#{expected_error_line}/) end end it "should pass when not receiving message specified as not to be received" do @mock.should_not_receive(:not_expected) @mock.rspec_verify end it "should pass when receiving message specified as not to be received with different args" do @mock.should_not_receive(:message).with("unwanted text") @mock.should_receive(:message).with("other text") @mock.message "other text" @mock.rspec_verify end it "should fail when receiving message specified as not to be received" do @mock.should_not_receive(:not_expected) lambda { @mock.not_expected violated }.should raise_error(MockExpectationError, "Mock \"test mock\" expected :not_expected with (no args) 0 times, but received it once") end it "should fail when receiving message specified as not to be received with args" do @mock.should_not_receive(:not_expected).with("unexpected text") lambda { @mock.not_expected("unexpected text") violated }.should raise_error(MockExpectationError, "Mock \"test mock\" expected :not_expected with (\"unexpected text\") 0 times, but received it once") end it "should pass when receiving message specified as not to be received with wrong args" do @mock.should_not_receive(:not_expected).with("unexpected text") @mock.not_expected "really unexpected text" @mock.rspec_verify end it "should allow block to calculate return values" do @mock.should_receive(:something).with("a","b","c").and_return { |a,b,c| c+b+a } @mock.something("a","b","c").should == "cba" @mock.rspec_verify end it "should allow parameter as return value" do @mock.should_receive(:something).with("a","b","c").and_return("booh") @mock.something("a","b","c").should == "booh" @mock.rspec_verify end it "should return nil if no return value set" do @mock.should_receive(:something).with("a","b","c") @mock.something("a","b","c").should be_nil @mock.rspec_verify end it "should raise exception if args don't match when method called" do @mock.should_receive(:something).with("a","b","c").and_return("booh") lambda { @mock.something("a","d","c") violated }.should raise_error(MockExpectationError, "Mock \"test mock\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") end it "should raise exception if args don't match when method called even when the method is stubbed" do @mock.stub!(:something) @mock.should_receive(:something).with("a","b","c") lambda { @mock.something("a","d","c") @mock.rspec_verify }.should raise_error(MockExpectationError, "Mock \"test mock\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: ([\"a\", \"d\", \"c\"])") end it "should raise exception if args don't match when method called even when using null_object" do @mock = mock("test mock", :null_object => true) @mock.should_receive(:something).with("a","b","c") lambda { @mock.something("a","d","c") @mock.rspec_verify }.should raise_error(MockExpectationError, "Mock \"test mock\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: ([\"a\", \"d\", \"c\"])") end it "should fail if unexpected method called" do lambda { @mock.something("a","b","c") violated }.should raise_error(MockExpectationError, "Mock \"test mock\" received unexpected message :something with (\"a\", \"b\", \"c\")") end it "should use block for expectation if provided" do @mock.should_receive(:something) do | a, b | a.should == "a" b.should == "b" "booh" end @mock.something("a", "b").should == "booh" @mock.rspec_verify end it "should fail if expectation block fails" do @mock.should_receive(:something) {| bool | bool.should be_true} lambda { @mock.something false }.should raise_error(MockExpectationError, /Mock "test mock" received :something but passed block failed with: expected false to be true/) end it "should fail right away when method defined as never is received" do @mock.should_receive(:not_expected).never lambda { @mock.not_expected }.should raise_error(MockExpectationError, "Mock \"test mock\" expected :not_expected with (no args) 0 times, but received it once") end it "should eventually fail when method defined as never is received" do @mock.should_receive(:not_expected).never lambda { @mock.not_expected }.should raise_error(MockExpectationError, "Mock \"test mock\" expected :not_expected with (no args) 0 times, but received it once") end it "should raise when told to" do @mock.should_receive(:something).and_raise(RuntimeError) lambda do @mock.something end.should raise_error(RuntimeError) end it "should raise passed an Exception instance" do error = RuntimeError.new("error message") @mock.should_receive(:something).and_raise(error) lambda { @mock.something }.should raise_error(RuntimeError, "error message") end it "should raise RuntimeError with passed message" do @mock.should_receive(:something).and_raise("error message") lambda { @mock.something }.should raise_error(RuntimeError, "error message") end it "should not raise when told to if args dont match" do @mock.should_receive(:something).with(2).and_raise(RuntimeError) lambda { @mock.something 1 }.should raise_error(MockExpectationError) end it "should throw when told to" do @mock.should_receive(:something).and_throw(:blech) lambda { @mock.something }.should throw_symbol(:blech) end it "should raise when explicit return and block constrained" do lambda { @mock.should_receive(:fruit) do |colour| :strawberry end.and_return :apple }.should raise_error(AmbiguousReturnError) end it "should ignore args on any args" do @mock.should_receive(:something).at_least(:once).with(any_args) @mock.something @mock.something 1 @mock.something "a", 2 @mock.something [], {}, "joe", 7 @mock.rspec_verify end it "should fail on no args if any args received" do @mock.should_receive(:something).with(no_args()) lambda { @mock.something 1 }.should raise_error(MockExpectationError, "Mock \"test mock\" received :something with unexpected arguments\n expected: (no args)\n got: (1)") end it "should fail when args are expected but none are received" do @mock.should_receive(:something).with(1) lambda { @mock.something }.should raise_error(MockExpectationError, "Mock \"test mock\" received :something with unexpected arguments\n expected: (1)\n got: (no args)") end it "should return value from block by default" do @mock.stub!(:method_that_yields).and_yield @mock.method_that_yields { :returned_obj }.should == :returned_obj @mock.rspec_verify end it "should yield 0 args to blocks that take a variable number of arguments" do @mock.should_receive(:yield_back).with(no_args()).once.and_yield a = nil @mock.yield_back {|*x| a = x} a.should == [] @mock.rspec_verify end it "should yield 0 args multiple times to blocks that take a variable number of arguments" do @mock.should_receive(:yield_back).once.with(no_args()).once.and_yield. and_yield a = nil b = [] @mock.yield_back {|*a| b << a} b.should == [ [], [] ] @mock.rspec_verify end it "should yield one arg to blocks that take a variable number of arguments" do @mock.should_receive(:yield_back).with(no_args()).once.and_yield(99) a = nil @mock.yield_back {|*x| a = x} a.should == [99] @mock.rspec_verify end it "should yield one arg 3 times consecutively to blocks that take a variable number of arguments" do @mock.should_receive(:yield_back).once.with(no_args()).once.and_yield(99). and_yield(43). and_yield("something fruity") a = nil b = [] @mock.yield_back {|*a| b << a} b.should == [[99], [43], ["something fruity"]] @mock.rspec_verify end it "should yield many args to blocks that take a variable number of arguments" do @mock.should_receive(:yield_back).with(no_args()).once.and_yield(99, 27, "go") a = nil @mock.yield_back {|*x| a = x} a.should == [99, 27, "go"] @mock.rspec_verify end it "should yield many args 3 times consecutively to blocks that take a variable number of arguments" do @mock.should_receive(:yield_back).once.with(no_args()).once.and_yield(99, :green, "go"). and_yield("wait", :amber). and_yield("stop", 12, :red) a = nil b = [] @mock.yield_back {|*a| b << a} b.should == [[99, :green, "go"], ["wait", :amber], ["stop", 12, :red]] @mock.rspec_verify end it "should yield single value" do @mock.should_receive(:yield_back).with(no_args()).once.and_yield(99) a = nil @mock.yield_back {|x| a = x} a.should == 99 @mock.rspec_verify end it "should yield single value 3 times consecutively" do @mock.should_receive(:yield_back).once.with(no_args()).once.and_yield(99). and_yield(43). and_yield("something fruity") a = nil b = [] @mock.yield_back {|a| b << a} b.should == [99, 43, "something fruity"] @mock.rspec_verify end it "should yield two values" do @mock.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') a, b = nil @mock.yield_back {|x,y| a=x; b=y} a.should == 'wha' b.should == 'zup' @mock.rspec_verify end it "should yield two values 3 times consecutively" do @mock.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). and_yield('not', 'down'). and_yield(14, 65) a, b = nil c = [] @mock.yield_back {|a,b| c << [a, b]} c.should == [['wha', 'zup'], ['not', 'down'], [14, 65]] @mock.rspec_verify end it "should fail when calling yielding method with wrong arity" do @mock.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') lambda { @mock.yield_back {|a|} }.should raise_error(MockExpectationError, "Mock \"test mock\" yielded |\"wha\", \"zup\"| to block with arity of 1") end it "should fail when calling yielding method consecutively with wrong arity" do @mock.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). and_yield('down'). and_yield(14, 65) lambda { a, b = nil c = [] @mock.yield_back {|a,b| c << [a, b]} }.should raise_error(MockExpectationError, "Mock \"test mock\" yielded |\"down\"| to block with arity of 2") end it "should fail when calling yielding method without block" do @mock.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') lambda { @mock.yield_back }.should raise_error(MockExpectationError, "Mock \"test mock\" asked to yield |[\"wha\", \"zup\"]| but no block was passed") end it "should be able to mock send" do @mock.should_receive(:send).with(any_args) @mock.send 'hi' @mock.rspec_verify end it "should be able to raise from method calling yielding mock" do @mock.should_receive(:yield_me).and_yield 44 lambda { @mock.yield_me do |x| raise "Bang" end }.should raise_error(StandardError, "Bang") @mock.rspec_verify end it "should clear expectations after verify" do @mock.should_receive(:foobar) @mock.foobar @mock.rspec_verify lambda { @mock.foobar }.should raise_error(MockExpectationError, "Mock \"test mock\" received unexpected message :foobar with (no args)") end it "should restore objects to their original state on rspec_reset" do mock = mock("this is a mock") mock.should_receive(:blah) mock.rspec_reset mock.rspec_verify #should throw if reset didn't work end it "should work even after method_missing starts raising NameErrors instead of NoMethodErrors" do # Object#method_missing throws either NameErrors or NoMethodErrors. # # On a fresh ruby program Object#method_missing: # * raises a NoMethodError when called directly # * raises a NameError when called indirectly # # Once Object#method_missing has been called at least once (on any object) # it starts behaving differently: # * raises a NameError when called directly # * raises a NameError when called indirectly # # There was a bug in Mock#method_missing that relied on the fact # that calling Object#method_missing directly raises a NoMethodError. # This example tests that the bug doesn't exist anymore. # Ensures that method_missing always raises NameErrors. a_method_that_doesnt_exist rescue @mock.should_receive(:foobar) @mock.foobar @mock.rspec_verify lambda { @mock.foobar }.should_not raise_error(NameError) lambda { @mock.foobar }.should raise_error(MockExpectationError) end it "should temporarily replace a method stub on a mock" do @mock.stub!(:msg).and_return(:stub_value) @mock.should_receive(:msg).with(:arg).and_return(:mock_value) @mock.msg(:arg).should equal(:mock_value) @mock.msg.should equal(:stub_value) @mock.msg.should equal(:stub_value) @mock.rspec_verify end it "should not require a different signature to replace a method stub" do @mock.stub!(:msg).and_return(:stub_value) @mock.should_receive(:msg).and_return(:mock_value) @mock.msg(:arg).should equal(:mock_value) @mock.msg.should equal(:stub_value) @mock.msg.should equal(:stub_value) @mock.rspec_verify end it "should raise an error when a previously stubbed method has a negative expectation" do @mock.stub!(:msg).and_return(:stub_value) @mock.should_not_receive(:msg).and_return(:mock_value) lambda {@mock.msg(:arg)}.should raise_error(MockExpectationError) end it "should temporarily replace a method stub on a non-mock" do non_mock = Object.new non_mock.stub!(:msg).and_return(:stub_value) non_mock.should_receive(:msg).with(:arg).and_return(:mock_value) non_mock.msg(:arg).should equal(:mock_value) non_mock.msg.should equal(:stub_value) non_mock.msg.should equal(:stub_value) non_mock.rspec_verify end it "should return the stubbed value when no new value specified" do @mock.stub!(:msg).and_return(:stub_value) @mock.should_receive(:msg) @mock.msg.should equal(:stub_value) @mock.rspec_verify end it "should return the stubbed value when stubbed with args and no new value specified" do @mock.stub!(:msg).with(:arg).and_return(:stub_value) @mock.should_receive(:msg).with(:arg) @mock.msg(:arg).should equal(:stub_value) @mock.rspec_verify end it "should not mess with the stub's yielded values when also mocked" do @mock.stub!(:yield_back).and_yield(:stub_value) @mock.should_receive(:yield_back).and_yield(:mock_value) @mock.yield_back{|v| v.should == :mock_value } @mock.yield_back{|v| v.should == :stub_value } @mock.rspec_verify end it "should yield multiple values after a similar stub" do File.stub!(:open).and_yield(:stub_value) File.should_receive(:open).and_yield(:first_call).and_yield(:second_call) yielded_args = [] File.open {|v| yielded_args << v } yielded_args.should == [:first_call, :second_call] File.open {|v| v.should == :stub_value } File.rspec_verify end it "should assign stub return values" do mock = Mock.new('name', :message => :response) mock.message.should == :response end end describe "a mock message receiving a block" do before(:each) do @mock = mock("mock") @calls = 0 end def add_call @calls = @calls + 1 end it "should call the block after #should_receive" do @mock.should_receive(:foo) { add_call } @mock.foo @calls.should == 1 end it "should call the block after #should_receive after a similar stub" do @mock.stub!(:foo).and_return(:bar) @mock.should_receive(:foo) { add_call } @mock.foo @calls.should == 1 end it "should call the block after #once" do @mock.should_receive(:foo).once { add_call } @mock.foo @calls.should == 1 end it "should call the block after #twice" do @mock.should_receive(:foo).twice { add_call } @mock.foo @mock.foo @calls.should == 2 end it "should call the block after #times" do @mock.should_receive(:foo).exactly(10).times { add_call } (1..10).each { @mock.foo } @calls.should == 10 end it "should call the block after #any_number_of_times" do @mock.should_receive(:foo).any_number_of_times { add_call } (1..7).each { @mock.foo } @calls.should == 7 end it "should call the block after #ordered" do @mock.should_receive(:foo).ordered { add_call } @mock.should_receive(:bar).ordered { add_call } @mock.foo @mock.bar @calls.should == 2 end end describe 'string representation generated by #to_s' do it 'should not contain < because that might lead to invalid HTML in some situations' do mock = mock("Dog") valid_html_str = "#{mock}" valid_html_str.should_not include('<') end end describe "mock created with no name" do it "should not use a name in a failure message" do mock = mock() expect {mock.foo}.to raise_error(/Mock received/) end it "should respond to initially stubbed methods" do mock = mock(:foo => "woo", :bar => "car") mock.foo.should == "woo" mock.bar.should == "car" end end describe "==" do it "sends '== self' to the comparison object" do first = mock('first') second = mock('second') first.should_receive(:==).with(second) second == first end end end end