From 54201512dfb436f37bfb0f0775ac53941085aa94 Mon Sep 17 00:00:00 2001 From: Pete Higgins Date: Tue, 21 May 2013 19:25:33 -0700 Subject: [PATCH 0001/1179] Fix method redefinition warnings. --- lib/rspec/mocks/instance_method_stasher.rb | 3 +++ lib/rspec/mocks/syntax.rb | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index 66b50077a..c3919dd57 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -75,6 +75,9 @@ def stashed_method_name def restore return unless @method_is_stashed + if @klass.__send__(:method_defined?, @method) + @klass.__send__(:undef_method, @method) + end @klass.__send__(:alias_method, @method, stashed_method_name) @klass.__send__(:remove_method, stashed_method_name) @method_is_stashed = false diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 902c97e4d..31b1f6b6a 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -60,9 +60,11 @@ def received_message?(message, *args, &block) ::RSpec::Mocks.proxy_for(self).received_message?(message, *args, &block) end - Class.class_eval do - def any_instance - ::RSpec::Mocks.any_instance_recorder_for(self) + unless Class.respond_to? :any_instance + Class.class_eval do + def any_instance + ::RSpec::Mocks.any_instance_recorder_for(self) + end end end end From be681610fb8da819d963041014fe41d896ad5730 Mon Sep 17 00:00:00 2001 From: Pete Higgins Date: Tue, 21 May 2013 22:56:07 -0700 Subject: [PATCH 0002/1179] Remove existing deprecation code in favor of that from rspec-expectations. --- lib/rspec/mocks.rb | 8 ------- lib/rspec/mocks/deprecation.rb | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 lib/rspec/mocks/deprecation.rb diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 251b526d9..f376faec3 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -29,14 +29,6 @@ def any_instance_recorder_for(klass) space.any_instance_recorder_for(klass) end - # @api private - # Used internally by RSpec to display custom deprecation warnings. This - # is also defined in rspec-core, but we can't assume it's loaded since - # rspec-expectations should be usable w/o rspec-core. - def warn_deprecation(message) - warn(message) - end - # @api private KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) diff --git a/lib/rspec/mocks/deprecation.rb b/lib/rspec/mocks/deprecation.rb new file mode 100644 index 000000000..935b0155f --- /dev/null +++ b/lib/rspec/mocks/deprecation.rb @@ -0,0 +1,38 @@ +module RSpec + unless respond_to?(:deprecate) + class << self + # Used internally by RSpec to display standard deprecation warnings. + # This is also defined in rspec-core, but we can't assume it's loaded + # since rspec-mocks should be usable w/o rspec-core. + def deprecate(method, alternate_method=nil, version=nil) + version_string = version ? "rspec-#{version}" : "a future version of RSpec" + + message = <<-NOTICE + +***************************************************************** +DEPRECATION WARNING: you are using deprecated behaviour that will +be removed from #{version_string}. + +#{caller(0)[2]} + +* #{method} is deprecated. +NOTICE + if alternate_method + message << <<-ADDITIONAL +* please use #{alternate_method} instead. +ADDITIONAL + end + + message << "*****************************************************************" + warn_deprecation(message) + end + + # Used internally by RSpec to display custom deprecation warnings. This + # is also defined in rspec-core, but we can't assume it's loaded since + # rspec-mocks should be usable w/o rspec-core. + def warn_deprecation(message) + warn(message) + end + end + end +end From 98588d0415b93855c57022888dd9960e7fbb16af Mon Sep 17 00:00:00 2001 From: Pete Higgins Date: Tue, 21 May 2013 22:56:46 -0700 Subject: [PATCH 0003/1179] Change all deprecation warnings to use new deprecation code. --- lib/rspec/mocks/example_methods.rb | 4 ++-- lib/rspec/mocks/message_expectation.rb | 13 +++---------- lib/rspec/mocks/syntax.rb | 4 ++-- lib/spec/mocks.rb | 2 +- spec/rspec/mocks/any_instance_spec.rb | 2 ++ spec/rspec/mocks/any_number_of_times_spec.rb | 4 ++-- spec/rspec/mocks/at_least_spec.rb | 4 ++-- spec/rspec/mocks/block_return_value_spec.rb | 13 ++++++++++++- spec/rspec/mocks/double_spec.rb | 4 ++-- spec/rspec/mocks/mock_spec.rb | 6 ++++-- spec/rspec/mocks/stub_spec.rb | 4 ++-- spec/rspec/mocks_spec.rb | 2 +- 12 files changed, 35 insertions(+), 27 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 213a8d5cd..1c8bd8f5a 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -33,13 +33,13 @@ def double(*args) # Deprecated: Use [double](#double-instance_method). def mock(*args) - RSpec::Mocks.warn_deprecation "\nDEPRECATION: use #double instead of #mock. #{caller(0)[1]}\n" + RSpec.deprecate "mock", "double" declare_double('Mock', *args) end # Deprecated: Use [double](#double-instance_method). def stub(*args) - RSpec::Mocks.warn_deprecation "\nDEPRECATION: use #double instead of #stub. #{caller(0)[1]}\n" + RSpec.deprecate "stub", "double" declare_double('Stub', *args) end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index ebed5650c..59b1adeac 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -316,9 +316,7 @@ def exactly(n, &block) # dealer.should_receive(:deal_card).at_least(9).times def at_least(n, &block) if n == 0 - RSpec::Mocks.warn_deprecation <<-MSG -DEPRECATION: at_least(0) is deprecated. Use #stub instead of #should_receive. Called from #{caller(0)[1]} -MSG + Rspec.deprecate "at_least(0) with should_receive", "stub" end @implementation = block if block @@ -353,9 +351,7 @@ def times(&block) # Allows an expected message to be received any number of times. def any_number_of_times(&block) - RSpec::Mocks.warn_deprecation <<-MSG -DEPRECATION: `#any_number_of_times` is deprecated, use `#stub` instead. Called from #{caller(0)[1]} -MSG + RSpec.deprecate "any_number_of_times", "stub" @implementation = block if block @expected_received_count = :any self @@ -459,10 +455,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub # no-op # @deprecated and_return is not supported with negative message expectations. def and_return(*) - RSpec::Mocks.warn_deprecation <<-MSG - -DEPRECATION: `and_return` with `should_not_receive` is deprecated. Called from #{caller(0)[1]} -MSG + RSpec.deprecate "and_return with should_not_receive" end # @private diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 902c97e4d..151050ae7 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -34,12 +34,12 @@ def unstub(message) end def stub!(message_or_hash, opts={}, &block) - ::RSpec::Mocks.warn_deprecation "\nDEPRECATION: use #stub instead of #stub!. #{caller(0)[1]}\n" + ::RSpec.deprecate "stub!", "stub" stub(message_or_hash, opts={}, &block) end def unstub!(message) - ::RSpec::Mocks.warn_deprecation "\nDEPRECATION: use #unstub instead of #unstub!. #{caller(0)[1]}\n" + ::RSpec.deprecate "unstub!", "unstub" unstub(message) end diff --git a/lib/spec/mocks.rb b/lib/spec/mocks.rb index 30058aabc..be3e28db2 100644 --- a/lib/spec/mocks.rb +++ b/lib/spec/mocks.rb @@ -1,6 +1,6 @@ # For backward compatibility with rspec-1 require 'rspec/mocks' -RSpec::Mocks.warn_deprecation "\nDEPRECATION: `require 'spec/mocks' " + +RSpec.warn_deprecation "\nDEPRECATION: `require 'spec/mocks' " + " is deprecated. Please require 'rspec/mocks' instead." diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 09b5efed9..06d7e723b 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -679,6 +679,8 @@ def foo; end end it "passes for a non-zero number of invocations" do + RSpec.should_receive(:warn_deprecation).with(/any_number_of_times/) + klass.any_instance.should_receive(:foo).any_number_of_times instance = klass.new instance.foo diff --git a/spec/rspec/mocks/any_number_of_times_spec.rb b/spec/rspec/mocks/any_number_of_times_spec.rb index b16e21658..2e92051b1 100644 --- a/spec/rspec/mocks/any_number_of_times_spec.rb +++ b/spec/rspec/mocks/any_number_of_times_spec.rb @@ -3,11 +3,11 @@ describe "AnyNumberOfTimes" do before(:each) do @mock = RSpec::Mocks::Mock.new("test mock") - RSpec::Mocks.stub(:warn_deprecation) + RSpec.stub(:warn_deprecation) end it "outputs a deprecation warning" do - RSpec::Mocks.should_receive(:warn_deprecation).with(/DEPRECATION: `#any_number_of_times` is deprecated/) + RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: any_number_of_times is deprecated/) @mock.should_receive(:random_call).any_number_of_times end diff --git a/spec/rspec/mocks/at_least_spec.rb b/spec/rspec/mocks/at_least_spec.rb index b121253e3..ad8c9b4bf 100644 --- a/spec/rspec/mocks/at_least_spec.rb +++ b/spec/rspec/mocks/at_least_spec.rb @@ -98,10 +98,10 @@ module Mocks end context "when sent with 0" do - before { RSpec::Mocks.stub(:warn_deprecation) } + before { RSpec.stub(:warn_deprecation) } it "outputs a deprecation warning" do - RSpec::Mocks.should_receive(:warn_deprecation).with(/at_least\(0\) is deprecated/) + RSpec.should_receive(:warn_deprecation).with(/at_least\(0\) with should_receive is deprecated/) @double.should_receive(:do_something).at_least(0).times end diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index fba610b0e..87711db0a 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -33,7 +33,7 @@ end end - %w[once twice any_number_of_times ordered and_return].each do |method| + %w[once twice ordered and_return].each do |method| describe method do it "returns the value of executing the block" do obj = Object.new @@ -43,6 +43,17 @@ end end + describe "any_number_of_times" do + it "returns the value of executing the block" do + RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: any_number_of_times is deprecated/) + + obj = Object.new + obj.stub(:foo).any_number_of_times { 'bar' } + expect(obj.foo).to eq('bar') + end + end + + describe "times" do it "returns the value of executing the block" do obj = Object.new diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 71600d4c3..2ea8a0bc8 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -12,12 +12,12 @@ describe "deprecated aliases" do it "warns if #stub is used" do - RSpec::Mocks.should_receive(:warn_deprecation).with(/DEPRECATION: use #double instead of #stub/) + RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: stub is deprecated.*use double instead/m) stub("TestDouble") end it "warns if #mock is used" do - RSpec::Mocks.should_receive(:warn_deprecation).with(/DEPRECATION: use #double instead of #mock/) + RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: mock is deprecated.*use double instead/m) mock("TestDouble") end end diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 399f92ce3..6418343c9 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -43,8 +43,8 @@ module Mocks end it "warns when should_not_receive is followed by and_return" do - RSpec::Mocks.should_receive(:warn_deprecation). - with(/`and_return` with `should_not_receive` is deprecated/) + RSpec.should_receive(:warn_deprecation). + with(/and_return with should_not_receive is deprecated/) @double.should_not_receive(:do_something).and_return(1) end @@ -623,6 +623,8 @@ def add_call end it "calls the block after #any_number_of_times" do + RSpec.should_receive(:warn_deprecation).with(/any_number_of_times/) + @double.should_receive(:foo).any_number_of_times { add_call } (1..7).each { @double.foo } diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 94a1b1d11..8c3834a02 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -39,7 +39,7 @@ def existing_private_instance_method describe "using stub!" do it "warns of deprecation but still returns the declared value when message is received" do - RSpec::Mocks.should_receive(:warn_deprecation).with(/DEPRECATION: use #stub instead of #stub!/) + RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: stub! is deprecated.*use stub instead/m) @instance.stub!(:msg).and_return(:return_value) expect(@instance.msg).to equal(:return_value) verify @instance @@ -57,7 +57,7 @@ def existing_private_instance_method describe 'using unstub!' do it 'removes the message stub but warns about deprecation' do @instance.stub(:msg) - RSpec::Mocks.should_receive(:warn_deprecation).with(/DEPRECATION: use #unstub instead of #unstub!/) + RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: unstub! is deprecated.*use unstub instead/m) @instance.unstub!(:msg) expect { @instance.msg }.to raise_error NoMethodError end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 50747e0a1..9056f41c8 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -58,7 +58,7 @@ context 'when requiring spec/mocks (as was valid for rspec 1)' do it 'prints a deprecation warning' do - ::RSpec::Mocks.should_receive(:warn_deprecation). + ::RSpec.should_receive(:warn_deprecation). with(%r|spec/mocks|) load "spec/mocks.rb" From d5fa19611fa6859d75e5eceb4be6e8b49ae15df6 Mon Sep 17 00:00:00 2001 From: David Chelimsky Date: Wed, 22 May 2013 20:10:47 -0500 Subject: [PATCH 0004/1179] update to the new deprecation API --- lib/rspec/mocks/deprecation.rb | 46 ++++++-------------- lib/rspec/mocks/example_methods.rb | 4 +- lib/rspec/mocks/message_expectation.rb | 4 +- lib/rspec/mocks/syntax.rb | 4 +- lib/rspec/mocks/test_double.rb | 2 +- lib/spec/mocks.rb | 3 +- spec/rspec/mocks/any_instance_spec.rb | 2 +- spec/rspec/mocks/any_number_of_times_spec.rb | 8 ++-- spec/rspec/mocks/at_least_spec.rb | 6 +-- spec/rspec/mocks/block_return_value_spec.rb | 2 +- spec/rspec/mocks/double_spec.rb | 4 +- spec/rspec/mocks/mock_spec.rb | 5 +-- spec/rspec/mocks/stub_spec.rb | 4 +- spec/rspec/mocks_spec.rb | 4 +- 14 files changed, 37 insertions(+), 61 deletions(-) diff --git a/lib/rspec/mocks/deprecation.rb b/lib/rspec/mocks/deprecation.rb index 935b0155f..50ff01d3c 100644 --- a/lib/rspec/mocks/deprecation.rb +++ b/lib/rspec/mocks/deprecation.rb @@ -1,38 +1,18 @@ module RSpec - unless respond_to?(:deprecate) - class << self - # Used internally by RSpec to display standard deprecation warnings. - # This is also defined in rspec-core, but we can't assume it's loaded - # since rspec-mocks should be usable w/o rspec-core. - def deprecate(method, alternate_method=nil, version=nil) - version_string = version ? "rspec-#{version}" : "a future version of RSpec" - - message = <<-NOTICE - -***************************************************************** -DEPRECATION WARNING: you are using deprecated behaviour that will -be removed from #{version_string}. - -#{caller(0)[2]} - -* #{method} is deprecated. -NOTICE - if alternate_method - message << <<-ADDITIONAL -* please use #{alternate_method} instead. -ADDITIONAL - end - - message << "*****************************************************************" - warn_deprecation(message) - end - - # Used internally by RSpec to display custom deprecation warnings. This - # is also defined in rspec-core, but we can't assume it's loaded since - # rspec-mocks should be usable w/o rspec-core. - def warn_deprecation(message) - warn(message) + module Mocks + module Deprecation + # @private + # + # Used internally to print deprecation warnings + def deprecate(deprecated, options={}) + message = "DEPRECATION: #{deprecated} is deprecated." + message << " Use #{options[:replacement]} instead." if options[:replacement] + message << " Called from #{caller(0)[2]}." + warn message end end end + + extend(Mocks::Deprecation) unless respond_to?(:deprecate) end + diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 1c8bd8f5a..d49f5bf9d 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -33,13 +33,13 @@ def double(*args) # Deprecated: Use [double](#double-instance_method). def mock(*args) - RSpec.deprecate "mock", "double" + RSpec.deprecate "mock", :replacement => "double" declare_double('Mock', *args) end # Deprecated: Use [double](#double-instance_method). def stub(*args) - RSpec.deprecate "stub", "double" + RSpec.deprecate "stub", :replacement => "double" declare_double('Stub', *args) end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 59b1adeac..d19dd796e 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -316,7 +316,7 @@ def exactly(n, &block) # dealer.should_receive(:deal_card).at_least(9).times def at_least(n, &block) if n == 0 - Rspec.deprecate "at_least(0) with should_receive", "stub" + RSpec.deprecate "at_least(0) with should_receive", :replacement => "stub" end @implementation = block if block @@ -351,7 +351,7 @@ def times(&block) # Allows an expected message to be received any number of times. def any_number_of_times(&block) - RSpec.deprecate "any_number_of_times", "stub" + RSpec.deprecate "any_number_of_times", :replacement => "stub" @implementation = block if block @expected_received_count = :any self diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 1067057ae..e8b6053f0 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -34,12 +34,12 @@ def unstub(message) end def stub!(message_or_hash, opts={}, &block) - ::RSpec.deprecate "stub!", "stub" + ::RSpec.deprecate "stub!", :replacement => "stub" stub(message_or_hash, opts={}, &block) end def unstub!(message) - ::RSpec.deprecate "unstub!", "unstub" + ::RSpec.deprecate "unstub!", :replacement => "unstub" unstub(message) end diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index a143f7d14..0f5ca07fd 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -93,7 +93,7 @@ def method_missing(message, *args, &block) def extract_options(stubs_and_options) if stubs_and_options[:null_object] @null_object = stubs_and_options.delete(:null_object) - RSpec.deprecate(%Q["double('name', :null_object => true)"], %Q["double('name').as_null_object"]) + RSpec.deprecate("double('name', :null_object => true)", :replacement => "double('name').as_null_object") end options = {} extract_option(stubs_and_options, options, :__declared_as, 'Mock') diff --git a/lib/spec/mocks.rb b/lib/spec/mocks.rb index be3e28db2..ebe976988 100644 --- a/lib/spec/mocks.rb +++ b/lib/spec/mocks.rb @@ -1,6 +1,5 @@ # For backward compatibility with rspec-1 require 'rspec/mocks' -RSpec.warn_deprecation "\nDEPRECATION: `require 'spec/mocks' " + - " is deprecated. Please require 'rspec/mocks' instead." +RSpec.deprecate "require 'spec/mocks'", :replacement => "require 'rspec/mocks'" diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 06d7e723b..8bba07c5f 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -679,7 +679,7 @@ def foo; end end it "passes for a non-zero number of invocations" do - RSpec.should_receive(:warn_deprecation).with(/any_number_of_times/) + allow(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") klass.any_instance.should_receive(:foo).any_number_of_times instance = klass.new diff --git a/spec/rspec/mocks/any_number_of_times_spec.rb b/spec/rspec/mocks/any_number_of_times_spec.rb index 2e92051b1..5b0745f12 100644 --- a/spec/rspec/mocks/any_number_of_times_spec.rb +++ b/spec/rspec/mocks/any_number_of_times_spec.rb @@ -3,12 +3,12 @@ describe "AnyNumberOfTimes" do before(:each) do @mock = RSpec::Mocks::Mock.new("test mock") - RSpec.stub(:warn_deprecation) + allow(RSpec).to receive(:deprecate) end - it "outputs a deprecation warning" do - RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: any_number_of_times is deprecated/) - @mock.should_receive(:random_call).any_number_of_times + it "is deprecated" do + expect(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") + expect(@mock).to receive(:random_call).any_number_of_times end it "passes if any number of times method is called many times" do diff --git a/spec/rspec/mocks/at_least_spec.rb b/spec/rspec/mocks/at_least_spec.rb index ad8c9b4bf..706aa2c11 100644 --- a/spec/rspec/mocks/at_least_spec.rb +++ b/spec/rspec/mocks/at_least_spec.rb @@ -98,11 +98,11 @@ module Mocks end context "when sent with 0" do - before { RSpec.stub(:warn_deprecation) } + before { RSpec.stub(:deprecate) } it "outputs a deprecation warning" do - RSpec.should_receive(:warn_deprecation).with(/at_least\(0\) with should_receive is deprecated/) - @double.should_receive(:do_something).at_least(0).times + expect(RSpec).to receive(:deprecate).with("at_least\(0\) with should_receive", :replacement => "stub") + expect(@double).to receive(:do_something).at_least(0).times end it "passes with no return if called once" do diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 87711db0a..5fdd63eee 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -45,7 +45,7 @@ describe "any_number_of_times" do it "returns the value of executing the block" do - RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: any_number_of_times is deprecated/) + expect(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") obj = Object.new obj.stub(:foo).any_number_of_times { 'bar' } diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 2ea8a0bc8..749a0338a 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -12,12 +12,12 @@ describe "deprecated aliases" do it "warns if #stub is used" do - RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: stub is deprecated.*use double instead/m) + expect(RSpec).to receive(:deprecate).with("stub", :replacement => "double") stub("TestDouble") end it "warns if #mock is used" do - RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: mock is deprecated.*use double instead/m) + expect(RSpec).to receive(:deprecate).with("mock", :replacement => "double") mock("TestDouble") end end diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 6418343c9..217166c37 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -43,8 +43,7 @@ module Mocks end it "warns when should_not_receive is followed by and_return" do - RSpec.should_receive(:warn_deprecation). - with(/and_return with should_not_receive is deprecated/) + expect(RSpec).to receive(:deprecate).with("and_return with should_not_receive") @double.should_not_receive(:do_something).and_return(1) end @@ -623,7 +622,7 @@ def add_call end it "calls the block after #any_number_of_times" do - RSpec.should_receive(:warn_deprecation).with(/any_number_of_times/) + expect(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") @double.should_receive(:foo).any_number_of_times { add_call } diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 8c3834a02..50d25eb33 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -39,7 +39,7 @@ def existing_private_instance_method describe "using stub!" do it "warns of deprecation but still returns the declared value when message is received" do - RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: stub! is deprecated.*use stub instead/m) + expect(RSpec).to receive(:deprecate).with("stub!", :replacement => "stub") @instance.stub!(:msg).and_return(:return_value) expect(@instance.msg).to equal(:return_value) verify @instance @@ -57,7 +57,7 @@ def existing_private_instance_method describe 'using unstub!' do it 'removes the message stub but warns about deprecation' do @instance.stub(:msg) - RSpec.should_receive(:warn_deprecation).with(/DEPRECATION: unstub! is deprecated.*use unstub instead/m) + RSpec.should_receive(:deprecate).with("unstub!", :replacement => "unstub") @instance.unstub!(:msg) expect { @instance.msg }.to raise_error NoMethodError end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 9056f41c8..1b2f1debe 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -58,9 +58,7 @@ context 'when requiring spec/mocks (as was valid for rspec 1)' do it 'prints a deprecation warning' do - ::RSpec.should_receive(:warn_deprecation). - with(%r|spec/mocks|) - + expect(::RSpec).to receive(:deprecate).with("require 'spec/mocks'", :replacement => "require 'rspec/mocks'") load "spec/mocks.rb" end end From 813cc058400ed8c732e1abc8b2a7218c816b1fa6 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 23 May 2013 09:03:32 +1000 Subject: [PATCH 0005/1179] fix an internal spec --- spec/rspec/mocks/block_return_value_spec.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 5fdd63eee..b30528071 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -43,17 +43,23 @@ end end - describe "any_number_of_times" do - it "returns the value of executing the block" do + describe 'any_number_of_times' do + before do + RSpec.stub(:warn_deprecation) + end + + it "warns about deprecation" do expect(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") + Object.new.stub(:foo).send(:any_number_of_times) { 'bar' } + end + it "returns the value of executing the block" do obj = Object.new - obj.stub(:foo).any_number_of_times { 'bar' } + obj.stub(:foo).send(:any_number_of_times) { 'bar' } expect(obj.foo).to eq('bar') end end - describe "times" do it "returns the value of executing the block" do obj = Object.new From a5e145a3ae0423520b2286e14bdea91f080dbd22 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 20 May 2013 10:00:02 +1000 Subject: [PATCH 0006/1179] defer to rspec-core when available for warning about deprecations --- lib/rspec/mocks.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index f376faec3..382472d67 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -2,6 +2,17 @@ require 'rspec/mocks/version' module RSpec + + # @api private + # Used internally by RSpec to display custom deprecation warnings. This + # is also defined in rspec-core, but we can't assume it's loaded since + # rspec-expectations should be usable w/o rspec-core. + unless RSpec.respond_to? :warn_deprecation + def warn_deprecation(message) + warn(message) + end + end + module Mocks class << self attr_accessor :space From 25724af1e32ba6869968403882c42dc7a37f4e77 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 23 May 2013 09:19:04 +1000 Subject: [PATCH 0007/1179] move deprecation message inline with core --- lib/rspec/mocks.rb | 10 ---------- lib/rspec/mocks/framework.rb | 2 +- lib/spec/mocks.rb | 1 - 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 382472d67..1fa5bc303 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -3,16 +3,6 @@ module RSpec - # @api private - # Used internally by RSpec to display custom deprecation warnings. This - # is also defined in rspec-core, but we can't assume it's loaded since - # rspec-expectations should be usable w/o rspec-core. - unless RSpec.respond_to? :warn_deprecation - def warn_deprecation(message) - warn(message) - end - end - module Mocks class << self attr_accessor :space diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 49f9febc0..b487ede57 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -2,6 +2,7 @@ # supports wrapping rspec's mocking functionality without invading every # object in the system. +require 'rspec/mocks/deprecation' require 'rspec/mocks/extensions/instance_exec' require 'rspec/mocks/instance_method_stasher' require 'rspec/mocks/method_double' @@ -31,4 +32,3 @@ require 'rspec/mocks/targets' require 'rspec/mocks/syntax' require 'rspec/mocks/configuration' - diff --git a/lib/spec/mocks.rb b/lib/spec/mocks.rb index ebe976988..a6ca01e75 100644 --- a/lib/spec/mocks.rb +++ b/lib/spec/mocks.rb @@ -2,4 +2,3 @@ require 'rspec/mocks' RSpec.deprecate "require 'spec/mocks'", :replacement => "require 'rspec/mocks'" - From ea65f44b967f82419482e6c3b4f327cef894d942 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 23 May 2013 14:03:23 +1000 Subject: [PATCH 0008/1179] fixup a send into a discrete call --- spec/rspec/mocks/block_return_value_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index b30528071..cfd380a72 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -50,12 +50,12 @@ it "warns about deprecation" do expect(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") - Object.new.stub(:foo).send(:any_number_of_times) { 'bar' } + Object.new.stub(:foo).any_number_of_times { 'bar' } end it "returns the value of executing the block" do obj = Object.new - obj.stub(:foo).send(:any_number_of_times) { 'bar' } + obj.stub(:foo).any_number_of_times { 'bar' } expect(obj.foo).to eq('bar') end end From cecd0f12966906e6d1560ace958f9becbcc573de Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 23 May 2013 23:29:37 +1000 Subject: [PATCH 0009/1179] fix up spec to remove warning --- spec/rspec/mocks/block_return_value_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index cfd380a72..8799174d6 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -45,7 +45,7 @@ describe 'any_number_of_times' do before do - RSpec.stub(:warn_deprecation) + RSpec.stub(:deprecate) end it "warns about deprecation" do From ee31fe5cd39cba52227dfe5ffbc48ee5a8879f31 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 24 May 2013 00:06:47 +1000 Subject: [PATCH 0010/1179] feature demonstrating standalone compatability --- .../step_definitions/additional_cli_steps.rb | 7 +++ features/test_frameworks/test_unit.feature | 43 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 features/test_frameworks/test_unit.feature diff --git a/features/step_definitions/additional_cli_steps.rb b/features/step_definitions/additional_cli_steps.rb index 99d9da884..22cd0296a 100644 --- a/features/step_definitions/additional_cli_steps.rb +++ b/features/step_definitions/additional_cli_steps.rb @@ -2,3 +2,10 @@ step %q{the output should contain "0 failures"} step %q{the exit status should be 0} end + +# Useful for when the output is slightly different on different versions of ruby +Then /^the output should contain "([^"]*)" or "([^"]*)"$/ do |string1, string2| + unless [string1, string2].any? { |s| all_output =~ regexp(s) } + fail %Q{Neither "#{string1}" or "#{string2}" were found in:\n#{all_output}} + end +end diff --git a/features/test_frameworks/test_unit.feature b/features/test_frameworks/test_unit.feature new file mode 100644 index 000000000..4897e7a80 --- /dev/null +++ b/features/test_frameworks/test_unit.feature @@ -0,0 +1,43 @@ +Feature: Test::Unit integration + + rspec-mocks is a stand-alone gem that can be used without the rest of + RSpec. If you like the way Test::Unit (or MiniTest) organizes tests, but + prefer RSpec's approach to mocking/stubbing/doubles etc, you can have both. + + The one downside is that failures are reported as errors with MiniTest. + + Scenario: use rspec/mocks with Test::Unit + Given a file named "rspec_mocks_test.rb" with: + """ruby + require 'test/unit' + require 'rspec/mocks' + + class RSpecMocksTest < Test::Unit::TestCase + def setup + RSpec::Mocks.setup(Object) + RSpec::Mocks.setup(self) + end + + def test_passing_expectation + obj = Object.new + expect(obj).to receive(:message) + obj.message + end + + def test_failing_expectation + obj = Object.new + expect(obj).to_not receive(:message) + obj.message + end + + def test_with_deprecation_warning + obj = Object.new + obj.stub(:old_message) { RSpec.deprecate(:old_message, :replacement => :message) } + obj.old_message + end + end + """ + When I run `ruby rspec_mocks_test.rb` + Then the output should contain "3 tests, 0 assertions, 0 failures, 1 errors" or "3 tests, 0 assertions, 1 failures, 0 errors" + And the output should contain "expected: 0 times with any arguments" + And the output should contain "old_message is deprecated. Use message instead." From 758ec5b04b11e5539441495b04c1a37eb3c3e27f Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 13 May 2013 10:04:29 +1000 Subject: [PATCH 0011/1179] nasty hack for 186 --- lib/rspec/mocks/method_double.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 5159515d7..812d9f771 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -63,8 +63,18 @@ def original_method # will handle this message or not...but we can at least try. # If it's not handled, a `NoMethodError` will be raised, just # like normally. - Proc.new do |*args, &block| - @object.__send__(:method_missing, @method_name, *args, &block) + if RUBY_VERSION == "1.8.6" + eval """ + Proc.new do |*args| + @object.__send__(:method_missing, @method_name, *args) + end + """ + else + eval """ + Proc.new do |*args, &block| + @object.__send__(:method_missing, @method_name, *args, &block) + end + """ end end From 77fa7a8c1b8560e3c0a97aa73b08dbcea52a0a4a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 24 May 2013 10:03:59 +1000 Subject: [PATCH 0012/1179] simpler fix --- lib/rspec/mocks/method_double.rb | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 812d9f771..0a0621cef 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -40,6 +40,16 @@ def visibility end end + class ProcWithBlock + def initialize(object,method) + @object, @method = object, method + end + + def call(*args,&block) + @object.__send__(:method_missing, @method, *args, &block) + end + end + # @private def original_method if @method_stasher.method_is_stashed? @@ -63,19 +73,7 @@ def original_method # will handle this message or not...but we can at least try. # If it's not handled, a `NoMethodError` will be raised, just # like normally. - if RUBY_VERSION == "1.8.6" - eval """ - Proc.new do |*args| - @object.__send__(:method_missing, @method_name, *args) - end - """ - else - eval """ - Proc.new do |*args, &block| - @object.__send__(:method_missing, @method_name, *args, &block) - end - """ - end + ProcWithBlock.new(@object,@method_name) end def original_unrecorded_any_instance_method From 15a0651b22196339914bae043006eed6229da135 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 27 May 2013 09:10:20 +1000 Subject: [PATCH 0013/1179] refactor for formatting --- lib/rspec/mocks/method_double.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 0a0621cef..64b6a0336 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -40,14 +40,12 @@ def visibility end end - class ProcWithBlock - def initialize(object,method) - @object, @method = object, method - end + class ProcWithBlock < Struct.new(:object, :method_name) - def call(*args,&block) - @object.__send__(:method_missing, @method, *args, &block) + def call(*args, &block) + self.object.__send__(:method_missing, self.method_name, *args, &block) end + end # @private From 82dbb16cd0462ae2bd39f1fc8a19c0b6bf3e4bd9 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 27 May 2013 09:27:00 +1000 Subject: [PATCH 0014/1179] regress to string eval until we can drop support for 186 --- lib/rspec/mocks/matchers/receive.rb | 10 ++++++---- lib/rspec/mocks/targets.rb | 8 +++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 5cab5ee18..89082da29 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -45,10 +45,12 @@ def setup_any_instance_allowance(subject, &block) MessageExpectation.public_instance_methods(false).each do |method| next if method_defined?(method) - define_method method do |*args, &block| - @recorded_customizations << Customization.new(method, args, block) - self - end + class_eval(<<-RUBY) + def #{method}(*args, &block) + @recorded_customizations << Customization.new(#{method.inspect}, args, block) + self + end + RUBY end private diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index ff18b2b10..4430d014a 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -10,14 +10,16 @@ def initialize(target) def self.delegate_to(matcher_method, options = {}) method_name = options.fetch(:from) { :to } - define_method method_name do |matcher, &block| + class_eval(<<-RUBY) + def #{method_name}(matcher, &block) unless Matchers::Receive === matcher raise UnsupportedMatcherError, "only the `receive` matcher is supported " + - "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" + "with `\#{expression}(...).\#{#{method_name.inspect}}`, but you have provided: \#{matcher}" end - matcher.__send__(matcher_method, @target, &block) + matcher.__send__(#{matcher_method.inspect}, @target, &block) end + RUBY end def self.disallow_negation(method) From 0db5a51c5901992ff55021f73db821f1bebf5770 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 27 May 2013 08:59:50 -0700 Subject: [PATCH 0015/1179] Ignore rbx 1.9 failures for now. Rbx failures have often been intermittent, unfortunately. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 845b38309..db5172e3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,4 +13,5 @@ rvm: matrix: allow_failures: - rvm: rbx-18mode + - rvm: rbx-19mode From 131099cfc17250a3f365806d7d4c01e1ce329690 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 27 May 2013 09:10:22 -0700 Subject: [PATCH 0016/1179] Prep changelog for 2.14.0.rc1 release. [ci skip] --- Changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8479f7d66..45c6ab48c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### dev -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...master) +### 2.14.0.rc1 / 2013-05-27 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) Enhancements: @@ -7,7 +7,7 @@ Enhancements: outside of the mocked object rather than inside it. This paves the way for future syntax enhancements and removes the need for some hacky work arounds for `any_instance` dup'ing and `YAML` serialization, - among other things. Note that the code now relies upon `object_id` + among other things. Note that the code now relies upon `__id__` returning a unique, consistent value for any object you want to mock or stub (Myron Marston). * Add support for test spies. This allows you to verify a message From 303bf0643ec4961800e827aac2d9bfb417b865a0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 27 May 2013 09:12:48 -0700 Subject: [PATCH 0017/1179] Release 2.14.0.rc1 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 88d696a1e..b0f6c46b3 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module Version - STRING = '2.13.0' + STRING = '2.14.0.rc1' end end end From 5e19fe91674067b2a253ab8b20d740cb5b32e651 Mon Sep 17 00:00:00 2001 From: Adarsh Pandit Date: Mon, 27 May 2013 09:20:18 -0700 Subject: [PATCH 0018/1179] Add documentation for Spies in README * Add documentation and code example for Test Spies implemented in PR #241 * Original PR https://github.com/rspec/rspec-mocks/pull/241 --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 46dc69acc..0845eb59c 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,26 @@ zipcode = Zipcode.new("02134", validator) zipcode.valid? ``` +## Test Spies + +Verifies the given object received the expected message during the course of the +test. The method must have previously been stubbed in order for messages to be +verified. + +Stubbing and verifying messages received in this way implements the Test Spy +pattern. + +```ruby + invitation = double('invitation', accept: true) + + user.accept_invitation(invitation) + + expect(invitation).to have_received(:accept) + + # You can also use most message expectations: + expect(invitation).to have_received(:accept).with(mailer).once +``` + ## Nomenclature ### Mock Objects and Test Stubs From 3eee26c79c49a99069e4655872e7f463a7221108 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 28 May 2013 08:52:52 +1000 Subject: [PATCH 0019/1179] fix for #293 by providing a hash and never nil --- lib/rspec/mocks/test_double.rb | 2 +- spec/rspec/mocks/test_double_spec.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 0f5ca07fd..5e7f94aa3 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -62,7 +62,7 @@ def respond_to?(message, incl_private=false) # @private def __build_mock_proxy - Proxy.new(self, @name, @options) + Proxy.new(self, @name, @options || {}) end private diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 6afe10c34..fbaf7b58d 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -51,6 +51,11 @@ def use; end double = Module.new { TestDouble.extend_onto(self) } expect { double.foo }.to raise_error(/Mock received/) end + + it 'warns of deprecation of :null_object => true' do + RSpec.should_receive :deprecate + double = Class.new { TestDouble.extend_onto self, 'name', :null_object => true } + end end end end From 80cbe187bf7d9447d408a2d09fc6a003e64687de Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 28 May 2013 08:53:51 +1000 Subject: [PATCH 0020/1179] update changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 45c6ab48c..1a0de7f51 100644 --- a/Changelog.md +++ b/Changelog.md @@ -37,6 +37,7 @@ Bug fixes (Luke Imhoff, Jon Rowe) * Fix isolation of `allow_message_expectations_on_nil` (Jon Rowe) * Use inspect to format actual arguments on expectations in failure messages (#280, Ben Langfeld) +* Protect against improperly initialised test doubles (#293) (Joseph Shraibman and Jon Rowe) Deprecations From 10d06c4e82b3a46f5c32efcda29bebd4d227c660 Mon Sep 17 00:00:00 2001 From: Adarsh Pandit Date: Mon, 27 May 2013 16:49:06 -0700 Subject: [PATCH 0021/1179] Cleaner language, add more code examples --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0845eb59c..537db267f 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,10 @@ pattern. expect(invitation).to have_received(:accept) - # You can also use most message expectations: - expect(invitation).to have_received(:accept).with(mailer).once + # You can also use other common message expectations. For example: + expect(invitation).to have_received(:accept).with(mailer) + expect(invitation).to have_received(:accept).twice + expect(invitation).to have_received(:accept).with(mailer).never ``` ## Nomenclature From 45582a9b7a55909283f8e89523c54f421535ffd2 Mon Sep 17 00:00:00 2001 From: Adarsh Pandit Date: Mon, 27 May 2013 16:59:12 -0700 Subject: [PATCH 0022/1179] Change deprecated `.never` to a `.to_not` message --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 537db267f..5dd481c21 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ pattern. # You can also use other common message expectations. For example: expect(invitation).to have_received(:accept).with(mailer) expect(invitation).to have_received(:accept).twice - expect(invitation).to have_received(:accept).with(mailer).never + expect(invitation).to_not have_received(:accept).with(mailer) ``` ## Nomenclature From 07f33e15db3ca8a55b5cd4045ccc280034dd6d74 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 28 May 2013 01:09:39 +0100 Subject: [PATCH 0023/1179] Add a changelog entry for #296. Signed-off-by: Sam Phippen --- Changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.md b/Changelog.md index 1a0de7f51..76136f161 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +### Development + +Enhancements: + +* Document test spies in the readme. (Adarsh Pandit) + ### 2.14.0.rc1 / 2013-05-27 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) From 29bdb9d97656b23b5582b436e5c62776433e3726 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 29 May 2013 00:50:35 +0100 Subject: [PATCH 0024/1179] Raise if a should_not_receive has an and_return. Signed-off-by: Sam Phippen --- Changelog.md | 5 +++++ lib/rspec/mocks/message_expectation.rb | 4 +--- spec/rspec/mocks/mock_spec.rb | 4 +--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 76136f161..bae9af0ac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,10 @@ ### Development +Breaking Changes for 3.0.0: + +* Raise an explicit error if `should_not_receive(...).and_return` is used. (Sam + Phippen) + Enhancements: * Document test spies in the readme. (Adarsh Pandit) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index d19dd796e..65945ed15 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -452,10 +452,8 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub super(error_generator, expectation_ordering, expected_from, method_double, 0, {}, &implementation) end - # no-op - # @deprecated and_return is not supported with negative message expectations. def and_return(*) - RSpec.deprecate "and_return with should_not_receive" + raise "and_return is not supported with negative message expectations" end # @private diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 217166c37..01885a766 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -43,9 +43,7 @@ module Mocks end it "warns when should_not_receive is followed by and_return" do - expect(RSpec).to receive(:deprecate).with("and_return with should_not_receive") - - @double.should_not_receive(:do_something).and_return(1) + expect { @double.should_not_receive(:do_something).and_return(1) }.to raise_error(/not supported/) end it "passes when receiving message specified as not to be received with different args" do From 14da850547a998dafc6f38b1b6eaa61d82105b6e Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 30 May 2013 01:52:58 +0100 Subject: [PATCH 0025/1179] Add an array_including argument matcher Closes #295. Signed-off-by: Sam Phippen --- Changelog.md | 1 + features/method_stubs/README.md | 3 ++ lib/rspec/mocks/argument_matchers.rb | 26 ++++++++++++ .../mocks/array_including_matcher_spec.rb | 41 +++++++++++++++++++ .../mocks/failing_argument_matchers_spec.rb | 14 +++++++ .../mocks/passing_argument_matchers_spec.rb | 5 +++ 6 files changed, 90 insertions(+) create mode 100644 spec/rspec/mocks/array_including_matcher_spec.rb diff --git a/Changelog.md b/Changelog.md index bae9af0ac..b08be83a0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Breaking Changes for 3.0.0: Enhancements: * Document test spies in the readme. (Adarsh Pandit) +* Add an `array_including` matcher. (Sam Phippen) ### 2.14.0.rc1 / 2013-05-27 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) diff --git a/features/method_stubs/README.md b/features/method_stubs/README.md index 32d36c085..014b2fc5e 100644 --- a/features/method_stubs/README.md +++ b/features/method_stubs/README.md @@ -41,6 +41,9 @@ You can also use the block format: obj.stub(:message).with(anything()) { ... } obj.stub(:message).with(an_instance_of(Money)) { ... } obj.stub(:message).with(hash_including(:a => 'b')) { ... } + obj.stub(:message).with(array_including(1,2,3)) { ... } + # or + obj.stub(:message).with(array_including([1,2,3])) { ... } #### Regular expressions diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 164a998f0..3778410e2 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -83,6 +83,20 @@ def description end end + class ArrayIncludingMatcher + def initialize(expected) + @expected = expected + end + + def ==(actual) + Set.new(actual).superset?(Set.new(@expected)) + end + + def description + "array_including(#{@expected.join(",")})" + end + end + class DuckTypeMatcher def initialize(*methods_to_respond_to) @methods_to_respond_to = methods_to_respond_to @@ -192,6 +206,18 @@ def hash_including(*args) HashIncludingMatcher.new(anythingize_lonely_keys(*args)) end + # Matches an array that includes the specified items at least once. + # Ignores duplicates and additional values + # + # @example + # + # object.should_receive(:message).with(array_including(1,2,3)) + # object.should_receive(:message).with(hash_including([1,2,3])) + def array_including(*args) + actually_an_array = args.first.is_a?(Array) && args.count == 1 ? args.first : args + ArrayIncludingMatcher.new(actually_an_array) + end + # Matches a hash that doesn't include the specified key(s) or key/value. # # @example diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb new file mode 100644 index 000000000..2f9046de8 --- /dev/null +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +module RSpec + module Mocks + module ArgumentMatchers + describe ArrayIncludingMatcher do + it "describes itself properly" do + expect(ArrayIncludingMatcher.new([1, 2, 3]).description).to eq "array_including(1,2,3)" + end + + context "passing" do + it "matches the same array" do + expect(array_including(1, 2, 3)).to eq([1, 2, 3]) + end + + it "matches the same array, specified without square brackets" do + expect(array_including(1, 2, 3)).to eq([1, 2, 3]) + end + + it "matches the same array, which includes nested arrays" do + expect(array_including([1, 2], 3, 4)).to eq([[1, 2], 3, 4]) + end + + it "works with duplicates in expected" do + expect(array_including(1, 1, 2, 3)).to eq([1, 2, 3]) + end + + it "works with duplicates in actual" do + expect(array_including(1, 2, 3)).to eq([1, 1, 2, 3]) + end + end + + context "failing" do + it "fails when not all the entries in the expected are present" do + expect(array_including(1,2,3,4,5)).not_to eq([1,2]) + end + end + end + end + end +end diff --git a/spec/rspec/mocks/failing_argument_matchers_spec.rb b/spec/rspec/mocks/failing_argument_matchers_spec.rb index c2ae42867..c31415cb5 100644 --- a/spec/rspec/mocks/failing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/failing_argument_matchers_spec.rb @@ -83,6 +83,20 @@ module Mocks end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (hash_including(:a=>1))\n got: ({})") end + it "fails array_including when args aren't array" do + expect do + @double.should_receive(:msg).with(array_including(1,2,3)) + @double.msg(1,2,3) + end.to raise_error(/array_including\(1,2,3\)/) + end + + it "fails array_including when arg doesn't contain all elements" do + expect do + @double.should_receive(:msg).with(array_including(1,2,3)) + @double.msg(1,2) + end.to raise_error(/array_including\(1,2,3\)/) + end + it "fails with block matchers" do expect do @double.should_receive(:msg).with {|arg| expect(arg).to eq :received } diff --git a/spec/rspec/mocks/passing_argument_matchers_spec.rb b/spec/rspec/mocks/passing_argument_matchers_spec.rb index 0b52de6f2..9d399fa2a 100644 --- a/spec/rspec/mocks/passing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/passing_argument_matchers_spec.rb @@ -82,6 +82,11 @@ module Mocks @double.should_receive(:random_call).with(hash_including(:a => 1)) @double.random_call(:a => 1) end + + it "matches array with array_including same array" do + @double.should_receive(:random_call).with(array_including(1,2)) + @double.random_call([1,2]) + end end context "handling block matchers" do From 5509cd92f1e351e510db3471f60ebc20f2d9963e Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 30 May 2013 18:05:58 +0100 Subject: [PATCH 0026/1179] Fix an array_including docstring. Signed-off-by: Sam Phippen --- lib/rspec/mocks/argument_matchers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 3778410e2..6e69306ec 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -212,7 +212,7 @@ def hash_including(*args) # @example # # object.should_receive(:message).with(array_including(1,2,3)) - # object.should_receive(:message).with(hash_including([1,2,3])) + # object.should_receive(:message).with(array_including([1,2,3])) def array_including(*args) actually_an_array = args.first.is_a?(Array) && args.count == 1 ? args.first : args ArrayIncludingMatcher.new(actually_an_array) From fbae36d6d090280aa0a41fe07dd0eab859713485 Mon Sep 17 00:00:00 2001 From: David Chelimsky Date: Wed, 5 Jun 2013 10:40:15 -0500 Subject: [PATCH 0027/1179] eliminate warning triggered in an example --- spec/rspec/mocks/test_double_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index fbaf7b58d..6f8f24455 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -53,8 +53,8 @@ def use; end end it 'warns of deprecation of :null_object => true' do - RSpec.should_receive :deprecate - double = Class.new { TestDouble.extend_onto self, 'name', :null_object => true } + expect(RSpec).to receive(:deprecate) + Class.new { TestDouble.extend_onto self, 'name', :null_object => true } end end end From 535999c8f35d931bf2fc5cca48b4a51689249a55 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 5 Jun 2013 15:49:43 +1000 Subject: [PATCH 0028/1179] coveralls setup --- Gemfile | 3 +++ spec/spec_helper.rb | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Gemfile b/Gemfile index 83adf413b..5c32c173f 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,9 @@ group :documentation do gem 'github-markup', '0.7.2' end +### dep for ci/coverage +gem 'coveralls', :require => false + platforms :jruby do gem "jruby-openssl" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eb51f08f2..132ff9098 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,6 +4,9 @@ rescue LoadError end +require 'coveralls' +Coveralls.wear! + RSpec::Matchers.define :include_method do |expected| match do |actual| actual.map { |m| m.to_s }.include?(expected.to_s) From 5c7ec80adc7dfec450e64beeb0ab18e5935251d4 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 5 Jun 2013 18:01:32 +1000 Subject: [PATCH 0029/1179] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dd481c21..ddc6a653f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) +# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) [![Coverage Status](https://coveralls.io/repos/rspec/rspec-mocks/badge.png?branch=master)](https://coveralls.io/r/rspec/rspec-mocks?branch=master) rspec-mocks is a test-double framework for rspec with support for method stubs, fakes, and message expectations on generated test-doubles and real objects From 988b6b1bcf3500d4fcf4815e5595ad5f883a8451 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 6 Jun 2013 12:06:55 +1000 Subject: [PATCH 0030/1179] tweak setup --- features/support/env.rb | 4 ++++ spec/spec_helper.rb | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/features/support/env.rb b/features/support/env.rb index 9f5898125..5708721f9 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -11,3 +11,7 @@ end end if RUBY_PLATFORM == 'java' +require 'coveralls' +Coveralls.wear! do + add_filter '/bundle/' +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 132ff9098..12035285f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,8 +4,11 @@ rescue LoadError end +require 'simplecov' if RUBY_VERSION.to_f > 1.8 require 'coveralls' -Coveralls.wear! +Coveralls.wear! do + add_filter '/bundle/' +end RSpec::Matchers.define :include_method do |expected| match do |actual| From 1f370af1021094161cc95e17a047d5dd5d3c3d18 Mon Sep 17 00:00:00 2001 From: Thomas Holmes Date: Thu, 6 Jun 2013 00:30:22 -0400 Subject: [PATCH 0031/1179] Update README.md to use expect syntax. This fixes rspec/rspec-mocks#300 --- README.md | 107 +++++++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 5dd481c21..4e6305f50 100644 --- a/README.md +++ b/README.md @@ -26,16 +26,15 @@ stubs can be declared on test doubles or real objects using the same syntax. rspec-mocks supports 3 forms for declaring method stubs: ```ruby -book.stub(:title) { "The RSpec Book" } -book.stub(:title => "The RSpec Book") -book.stub(:title).and_return("The RSpec Book") +allow(book).to receive(:title) { "The RSpec Book" } +allow(book).to receive(:title).and_return("The RSpec Book") ``` You can also use this shortcut, which creates a test double and declares a method stub in one statement: ```ruby -book = double("book", :title => "The RSpec Book") +book = double("book", title: "The RSpec Book") ``` The first argument is a name, which is used for documentation and appears in @@ -43,7 +42,7 @@ failure messages. If you don't care about the name, you can leave it out, making the combined instantiation/stub declaration very terse: ```ruby -double(:foo => 'bar') +double(foo: 'bar') ``` This is particularly nice when providing a list of test doubles to a method @@ -60,7 +59,7 @@ arguments to `and_return`. The invocations cycle through the list. The last value is returned for any subsequent invocations: ```ruby -die.stub(:roll).and_return(1,2,3) +allow(die).to receive(:roll).and_return(1,2,3) die.roll # => 1 die.roll # => 2 die.roll # => 3 @@ -71,7 +70,7 @@ die.roll # => 3 To return an array in a single invocation, declare an array: ```ruby -team.stub(:players).and_return([stub(:name => "David")]) +allow(team).to receive(:players).and_return([double(name: "David")]) ``` ## Message Expectations @@ -82,7 +81,7 @@ expectation is satisfied. If not, the example fails. ```ruby validator = double("validator") -validator.should_receive(:validate).with("02134") +expect(validator).to receive(:validate) { "02134" } zipcode = Zipcode.new("02134", validator) zipcode.valid? ``` @@ -134,7 +133,7 @@ For example, in Rails: ```ruby person = double("person") -Person.stub(:find) { person } +allow(Person).to receive(:find) { person } ``` In this case we're instrumenting Person to return the person object we've @@ -143,7 +142,7 @@ expectation so that the example fails if `find` is not called: ```ruby person = double("person") -Person.should_receive(:find) { person } +expect(Person).to receive(:find) { person } ``` We can do this with any object in a system because rspec-mocks adds the `stub` @@ -155,15 +154,15 @@ expectations, and then restores the original methods. ## Expecting Arguments ```ruby -double.should_receive(:msg).with(*args) -double.should_not_receive(:msg).with(*args) +expect(double).to receive(:msg).with(*args) +expect(double).to_not receive(:msg).with(*args) ``` You can set multiple expectations for the same message if you need to: ```ruby -double.should_receive(:msg).with("A", 1, 3) -double.should_receive(:msg).with("B", 2, 4) +expect(double).to receive(:msg).with("A", 1, 3) +expect(double).to receive(:msg).with("B", 2, 4) ``` ## Argument Matchers @@ -179,81 +178,81 @@ rspec-mocks also adds some keyword Symbols that you can use to specify certain kinds of arguments: ```ruby -double.should_receive(:msg).with(no_args()) -double.should_receive(:msg).with(any_args()) -double.should_receive(:msg).with(1, kind_of(Numeric), "b") #2nd argument can be any kind of Numeric -double.should_receive(:msg).with(1, boolean(), "b") #2nd argument can be true or false -double.should_receive(:msg).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp -double.should_receive(:msg).with(1, anything(), "b") #2nd argument can be anything at all -double.should_receive(:msg).with(1, duck_type(:abs, :div), "b") +expect(double).to receive(:msg).with(no_args()) +expect(double).to receive(:msg).with(any_args()) +expect(double).to receive(:msg).with(1, kind_of(Numeric), "b") #2nd argument can be any kind of Numeric +expect(double).to receive(:msg).with(1, boolean(), "b") #2nd argument can be true or false +expect(double).to receive(:msg).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp +expect(double).to receive(:msg).with(1, anything(), "b") #2nd argument can be anything at all +expect(double).to receive(:msg).with(1, duck_type(:abs, :div), "b") #2nd argument can be object that responds to #abs and #div ``` ## Receive Counts ```ruby -double.should_receive(:msg).once -double.should_receive(:msg).twice -double.should_receive(:msg).exactly(n).times -double.should_receive(:msg).at_least(:once) -double.should_receive(:msg).at_least(:twice) -double.should_receive(:msg).at_least(n).times -double.should_receive(:msg).at_most(:once) -double.should_receive(:msg).at_most(:twice) -double.should_receive(:msg).at_most(n).times -double.should_receive(:msg).any_number_of_times +expect(double).to receive(:msg).once +expect(double).to receive(:msg).twice +expect(double).to receive(:msg).exactly(n).times +expect(double).to receive(:msg).at_least(:once) +expect(double).to receive(:msg).at_least(:twice) +expect(double).to receive(:msg).at_least(n).times +expect(double).to receive(:msg).at_most(:once) +expect(double).to receive(:msg).at_most(:twice) +expect(double).to receive(:msg).at_most(n).times +expect(double).to receive(:msg).any_number_of_times ``` ## Ordering ```ruby -double.should_receive(:msg).ordered -double.should_receive(:other_msg).ordered +expect(double).to receive(:msg).ordered +expect(double).to receive(:other_msg).ordered #This will fail if the messages are received out of order ``` This can include the same message with different arguments: ```ruby -double.should_receive(:msg).with("A", 1, 3).ordered -double.should_receive(:msg).with("B", 2, 4).ordered +expect(double).to receive(:msg).with("A", 1, 3).ordered +expect(double).to receive(:msg).with("B", 2, 4).ordered ``` ## Setting Responses Whether you are setting a message expectation or a method stub, you can tell the object precisely how to respond. The most generic way is to pass -a block to `stub` or `should_receive`: +a block to `receive`: ```ruby -double.should_receive(:msg) { value } +expect(double).to receive(:msg) { value } ``` When the double receives the `msg` message, it evaluates the block and returns the result. ```ruby -double.should_receive(:msg).and_return(value) -double.should_receive(:msg).exactly(3).times.and_return(value1, value2, value3) +expect(double).to receive(:msg).and_return(value) +expect(double).to receive(:msg).exactly(3).times.and_return(value1, value2, value3) # returns value1 the first time, value2 the second, etc -double.should_receive(:msg).and_raise(error) +expect(double).to receive(:msg).and_raise(error) #error can be an instantiated object or a class #if it is a class, it must be instantiable with no args -double.should_receive(:msg).and_throw(:msg) -double.should_receive(:msg).and_yield(values,to,yield) -double.should_receive(:msg).and_yield(values,to,yield).and_yield(some,other,values,this,time) +expect(double).to receive(:msg).and_throw(:msg) +expect(double).to receive(:msg).and_yield(values,to,yield) +expect(double).to receive(:msg).and_yield(values,to,yield).and_yield(some,other,values,this,time) # for methods that yield to a block multiple times ``` Any of these responses can be applied to a stub as well ```ruby -double.stub(:msg).and_return(value) -double.stub(:msg).and_return(value1, value2, value3) -double.stub(:msg).and_raise(error) -double.stub(:msg).and_throw(:msg) -double.stub(:msg).and_yield(values,to,yield) -double.stub(:msg).and_yield(values,to,yield).and_yield(some,other,values,this,time) +allow(double).to receive(:msg).and_return(value) +allow(double).to receive(:msg).and_return(value1, value2, value3) +allow(double).to receive(:msg).and_raise(error) +allow(double).to receive(:msg).and_throw(:msg) +allow(double).to receive(:msg).and_yield(values,to,yield) +allow(double).to receive(:msg).and_yield(values,to,yield).and_yield(some,other,values,this,time) ``` ## Arbitrary Handling @@ -264,8 +263,8 @@ to come with an Array argument that has a specific length, but you don't care what is in it. You could do this: ```ruby -double.should_receive(:msg) do |arg| - arg.size.should eq(7) +expect(double).to receive(:msg) do |arg| + expect(arg.size).to eq 7 end ``` @@ -273,7 +272,7 @@ If the method being stubbed itself takes a block, and you need to yield to it in some special way, you can use this: ```ruby -double.should_receive(:msg) do |&arg| +expect(double).to receive(:msg) do |&arg| begin arg.call ensure @@ -290,7 +289,7 @@ the object responds to the message. You can use `and_call_original` to achieve this: ```ruby -Person.should_receive(:find).and_call_original +expect(Person).to receive(:find).and_call_original Person.find # => executes the original find method and returns the result ``` @@ -300,7 +299,7 @@ Combining the message name with specific arguments, receive counts and responses you can get quite a bit of detail in your expectations: ```ruby -double.should_receive(:<<).with("illegal value").once.and_raise(ArgumentError) +expect(double).to receive(:<<).with("illegal value").once.and_raise(ArgumentError) ``` While this is a good thing when you really need it, you probably don't really From 520cbe1be47af7bf323a268205eb2471230d1c67 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 6 Jun 2013 19:20:28 +1000 Subject: [PATCH 0032/1179] prevent coveralls errors from escaping --- features/support/env.rb | 11 ++++++++--- spec/spec_helper.rb | 12 ++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/features/support/env.rb b/features/support/env.rb index 5708721f9..997bcfaff 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -11,7 +11,12 @@ end end if RUBY_PLATFORM == 'java' -require 'coveralls' -Coveralls.wear! do - add_filter '/bundle/' +begin + require 'simplecov' + require 'coveralls' + Coveralls.wear_merged! do + add_filter '/bundle/' + end +rescue + warn "Coveralls failed to initialise" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 12035285f..354d602c5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,10 +4,14 @@ rescue LoadError end -require 'simplecov' if RUBY_VERSION.to_f > 1.8 -require 'coveralls' -Coveralls.wear! do - add_filter '/bundle/' +begin + require 'simplecov' + require 'coveralls' + Coveralls.wear_merged! do + add_filter '/bundle/' + end +rescue + warn "Coveralls failed to initialise" end RSpec::Matchers.define :include_method do |expected| From 0932dae1011702e9865c5f2ee61009bea78b5240 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 7 Jun 2013 12:33:31 +1000 Subject: [PATCH 0033/1179] try even harder to rescue coveralls --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 354d602c5..c4053bf5c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -10,7 +10,7 @@ Coveralls.wear_merged! do add_filter '/bundle/' end -rescue +rescue Exception => e warn "Coveralls failed to initialise" end From e523ee3dadd83bcd4fd29ab8ffbd21f69647c7ac Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 9 Jun 2013 09:30:09 +1000 Subject: [PATCH 0034/1179] make myrons improvements --- features/support/env.rb | 10 ---------- script/test_all | 2 +- spec/spec_helper.rb | 20 ++++++++++++-------- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/features/support/env.rb b/features/support/env.rb index 997bcfaff..89fe44636 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -10,13 +10,3 @@ set_env('JRUBY_OPTS', "-X-C #{ENV['JRUBY_OPTS']}") # disable JIT since these processes are so short lived end end if RUBY_PLATFORM == 'java' - -begin - require 'simplecov' - require 'coveralls' - Coveralls.wear_merged! do - add_filter '/bundle/' - end -rescue - warn "Coveralls failed to initialise" -end diff --git a/script/test_all b/script/test_all index cddb879d6..700625210 100755 --- a/script/test_all +++ b/script/test_all @@ -18,7 +18,7 @@ echo "--------------------------------------------------------------------" echo for file in `find spec -iname '*_spec.rb'`; do - bin/rspec $file --format progress + NO_COVERALLS=1 bin/rspec $file --format progress done # TODO: it would be nice to figure out how to run the cukes w/o the overhead of diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c4053bf5c..ac0884f70 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,14 +4,18 @@ rescue LoadError end -begin - require 'simplecov' - require 'coveralls' - Coveralls.wear_merged! do - add_filter '/bundle/' - end -rescue Exception => e - warn "Coveralls failed to initialise" +unless ENV['NO_COVERALLS'] + begin + require 'simplecov' + require 'coveralls' + Coveralls.wear_merged! do + add_filter '/bundle/' + add_filter '/spec/' + add_filter '/tmp/' + end + rescue Exception => e + warn "Coveralls failed to initialise" + end end RSpec::Matchers.define :include_method do |expected| From 19e03c90c528454c45810f1de8cb059aa36028a9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 9 Jun 2013 08:06:35 -0700 Subject: [PATCH 0035/1179] Coveralls tweaks. - No need for `wear_merged!` - Let's not `rescue Exception` unless we really need it. --- spec/spec_helper.rb | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ac0884f70..6717b97fa 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,16 +5,12 @@ end unless ENV['NO_COVERALLS'] - begin - require 'simplecov' - require 'coveralls' - Coveralls.wear_merged! do - add_filter '/bundle/' - add_filter '/spec/' - add_filter '/tmp/' - end - rescue Exception => e - warn "Coveralls failed to initialise" + require 'simplecov' + require 'coveralls' + Coveralls.wear! do + add_filter '/bundle/' + add_filter '/spec/' + add_filter '/tmp/' end end From 4993498fa6903cb794eab2d51c27aad2fa219902 Mon Sep 17 00:00:00 2001 From: Thomas Holmes Date: Tue, 11 Jun 2013 14:39:13 -0400 Subject: [PATCH 0036/1179] Put hashrockets back in place Keep using hashrockets in examples for rspec-mocks as they are syntax compatible with all supported versions of Ruby. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4e6305f50..8e809823b 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ You can also use this shortcut, which creates a test double and declares a method stub in one statement: ```ruby -book = double("book", title: "The RSpec Book") +book = double("book", :title => "The RSpec Book") ``` The first argument is a name, which is used for documentation and appears in @@ -42,7 +42,7 @@ failure messages. If you don't care about the name, you can leave it out, making the combined instantiation/stub declaration very terse: ```ruby -double(foo: 'bar') +double(:foo => 'bar') ``` This is particularly nice when providing a list of test doubles to a method @@ -70,7 +70,7 @@ die.roll # => 3 To return an array in a single invocation, declare an array: ```ruby -allow(team).to receive(:players).and_return([double(name: "David")]) +allow(team).to receive(:players).and_return([double(:name => "David")]) ``` ## Message Expectations @@ -96,7 +96,7 @@ Stubbing and verifying messages received in this way implements the Test Spy pattern. ```ruby - invitation = double('invitation', accept: true) + invitation = double('invitation', :accept => true) user.accept_invitation(invitation) From b76e497a385a02393d66c59818d1418ca1603c32 Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Wed, 12 Jun 2013 21:28:02 +1000 Subject: [PATCH 0037/1179] Bypass RSpec::Mocks::Syntax when mass-assigning stubs via double(). When mock_with's `config.syntax = [:expect]`, the `#stub` method is not available on the TestDouble. This change bypasses the pluggable syntax layer, directly setting up allowance for a Matcher::Receive instance. Another option would be to use AcceptanceTarget.new(self).to(matcher) however the `#to` DSL-like method feels out of place when not used with the `allow(target)` receiver syntax, and also the AcceptanceTarget delegation implementation complexity is not required for internals like this. --- lib/rspec/mocks/test_double.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 5e7f94aa3..a2509896a 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -110,7 +110,8 @@ def extract_option(source, target, key, default=nil) def assign_stubs(stubs) stubs.each_pair do |message, response| - stub(message).and_return(response) + matcher = Matchers::Receive.new(message, nil).and_return(response) + matcher.setup_allowance(self) end end From 40d259c548ee383dd8a89e482f3a0218cc06a97c Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Wed, 12 Jun 2013 22:08:08 +1000 Subject: [PATCH 0038/1179] Spec double() stubs hash in different syntax modes. This tests a regression referenced in #306 where disabling the :should syntax raised errors when a stubs hash was passed to double(). --- spec/rspec/mocks/test_double_spec.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 6f8f24455..7c16231cb 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -31,13 +31,18 @@ def use; end expect { double.foo }.to raise_error(/Mock "MyDouble" received/) end - it 'stubs the methods passed in the stubs hash' do - double = Module.new do - TestDouble.extend_onto(self, "MyDouble", :a => 5, :b => 10) - end + [[:should, :expect], [:expect], [:should]].each do |syntax| + context "with syntax #{syntax.inspect}" do + include_context "with syntax", syntax + it 'stubs the methods passed in the stubs hash' do + double = Module.new do + TestDouble.extend_onto(self, "MyDouble", :a => 5, :b => 10) + end - expect(double.a).to eq(5) - expect(double.b).to eq(10) + expect(double.a).to eq(5) + expect(double.b).to eq(10) + end + end end it 'indicates what type of test double it is in error messages' do From 38f19aefff0a612e51509d4a4bc8aadb3a026a93 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 13 Jun 2013 22:26:10 +1000 Subject: [PATCH 0039/1179] remove instance exec --- lib/rspec/mocks/extensions/instance_exec.rb | 34 --------------------- lib/rspec/mocks/framework.rb | 1 - lib/rspec/mocks/message_expectation.rb | 2 +- 3 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 lib/rspec/mocks/extensions/instance_exec.rb diff --git a/lib/rspec/mocks/extensions/instance_exec.rb b/lib/rspec/mocks/extensions/instance_exec.rb deleted file mode 100644 index eb7960bbb..000000000 --- a/lib/rspec/mocks/extensions/instance_exec.rb +++ /dev/null @@ -1,34 +0,0 @@ -module RSpec - module Mocks - # @private - module InstanceExec - unless respond_to?(:instance_exec) - # @private - # - # based on Bounded Spec InstanceExec (Mauricio Fernandez) - # http://eigenclass.org/hiki/bounded+space+instance_exec - # - uses singleton_class of matcher instead of global - # InstanceExecHelper module - # - this keeps it scoped to this class only, which is the - # only place we need it - # - only necessary for ruby 1.8.6 - def instance_exec(*args, &block) - singleton_class = (class << self; self; end) - begin - orig_critical, Thread.critical = Thread.critical, true - n = 0 - n += 1 while respond_to?(method_name="__instance_exec#{n}") - singleton_class.module_eval{ define_method(method_name, &block) } - ensure - Thread.critical = orig_critical - end - begin - return send(method_name, *args) - ensure - singleton_class.module_eval{ remove_method(method_name) } rescue nil - end - end - end - end - end -end diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index b487ede57..4665ea470 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -3,7 +3,6 @@ # object in the system. require 'rspec/mocks/deprecation' -require 'rspec/mocks/extensions/instance_exec' require 'rspec/mocks/instance_method_stasher' require 'rspec/mocks/method_double' require 'rspec/mocks/argument_matchers' diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 65945ed15..5d5dfaf18 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -152,7 +152,7 @@ def and_throw(*args) # # stream.stub(:open).and_yield(StringIO.new) def and_yield(*args, &block) - yield @eval_context = Object.new.extend(RSpec::Mocks::InstanceExec) if block + yield @eval_context = Object.new if block @args_to_yield << args @implementation = build_implementation self From 8ddbe507f4c328990fd11c98f63cda3716a4548a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 13 Jun 2013 22:29:35 +1000 Subject: [PATCH 0040/1179] remove 1.8.6 workaround on method owner --- lib/rspec/mocks/instance_method_stasher.rb | 45 ++++++++-------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index c3919dd57..53609428d 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -34,34 +34,23 @@ def method_defined_on_klass?(klass = @klass) klass.method_defined?(@method) || klass.private_method_defined?(@method) end - if ::UnboundMethod.method_defined?(:owner) - # @private - def method_owned_by_klass? - owner = @klass.instance_method(@method).owner - # On 1.8 (and some 1.9s -- e.g. rubinius) aliased methods - # can report the wrong owner. Example: - # class MyClass - # class << self - # alias alternate_new new - # end - # end - # - # MyClass.owner(:alternate_new) returns `Class` on 1.8, - # but we need to consider the owner to be `MyClass` because - # it is not actually available on `Class` but is on `MyClass`. - # Hence, we verify that the owner actually has the method defined. - # If the given owner does not have the method defined, we assume - # that the method is actually owned by @klass. - owner == @klass || !(method_defined_on_klass?(owner)) - end - else - # @private - def method_owned_by_klass? - # On 1.8.6, which does not support Method#owner, we have no choice but - # to assume it's defined on the klass even if it may be defined on - # a superclass. - true - end + def method_owned_by_klass? + owner = @klass.instance_method(@method).owner + # On some 1.9s (e.g. rubinius) aliased methods + # can report the wrong owner. Example: + # class MyClass + # class << self + # alias alternate_new new + # end + # end + # + # MyClass.owner(:alternate_new) returns `Class` when incorrect, + # but we need to consider the owner to be `MyClass` because + # it is not actually available on `Class` but is on `MyClass`. + # Hence, we verify that the owner actually has the method defined. + # If the given owner does not have the method defined, we assume + # that the method is actually owned by @klass. + owner == @klass || !(method_defined_on_klass?(owner)) end public From 2811c58a2c74377bdf3e9d6d77b36d94bf68d016 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 13 Jun 2013 22:32:35 +1000 Subject: [PATCH 0041/1179] remove spec workarounds --- spec/rspec/mocks/mock_spec.rb | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 01885a766..6a6a1f85c 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -197,28 +197,24 @@ def @double.method_with_default_argument(arg={}); end expect( Array(@double) ).to eq([@double]) end - context "with Ruby > 1.8.6", :unless => RUBY_VERSION.to_s == '1.8.6' do - it "passes proc to expectation block without an argument" do - # We eval this because Ruby 1.8.6's syntax parser barfs on { |&block| ... } - # and prevents the entire spec suite from running. - eval("@double.should_receive(:foo) {|&block| expect(block.call).to eq(:bar)}") - @double.foo { :bar } - end + it "passes proc to expectation block without an argument" do + @double.should_receive(:foo) { |&block| expect(block.call).to eq(:bar) } + @double.foo { :bar } + end - it "passes proc to expectation block with an argument" do - eval("@double.should_receive(:foo) {|arg, &block| expect(block.call).to eq(:bar)}") - @double.foo(:arg) { :bar } - end + it "passes proc to expectation block with an argument" do + @double.should_receive(:foo) { |arg, &block| expect(block.call).to eq(:bar) } + @double.foo(:arg) { :bar } + end - it "passes proc to stub block without an argurment" do - eval("@double.stub(:foo) {|&block| expect(block.call).to eq(:bar)}") - @double.foo { :bar } - end + it "passes proc to stub block without an argurment" do + @double.stub(:foo) { |&block| expect(block.call).to eq(:bar) } + @double.foo { :bar } + end - it "passes proc to stub block with an argument" do - eval("@double.stub(:foo) {|arg, &block| expect(block.call).to eq(:bar)}") - @double.foo(:arg) { :bar } - end + it "passes proc to stub block with an argument" do + @double.stub(:foo) { |arg, &block| expect(block.call).to eq(:bar) } + @double.foo(:arg) { :bar } end it "fails right away when method defined as never is received" do From 3812438f628b9b01677a0412dcc09f9c74c07a91 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 13 Jun 2013 15:44:00 +0100 Subject: [PATCH 0042/1179] Add a changelog entry for #306. Signed-off-by: Sam Phippen --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index b08be83a0..ab91c0677 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,10 @@ Enhancements: * Document test spies in the readme. (Adarsh Pandit) * Add an `array_including` matcher. (Sam Phippen) +Bug Fixes: + +* Bypass RSpec::Mocks::Syntax when mass-assigning stubs via double(). (Paul Annesley) + ### 2.14.0.rc1 / 2013-05-27 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) From 1214b6a0d8ab1a2093a2310426f71bb897a68487 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 14 Jun 2013 09:46:25 +1000 Subject: [PATCH 0043/1179] document dropping 1.8.6 --- Changelog.md | 1 + rspec-mocks.gemspec | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index b08be83a0..b25b831d6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Breaking Changes for 3.0.0: * Raise an explicit error if `should_not_receive(...).and_return` is used. (Sam Phippen) +* Remove 1.8.6 workarounds (Jon Rowe) Enhancements: diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index eda163354..52f83d943 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -21,6 +21,8 @@ Gem::Specification.new do |s| s.rdoc_options = ["--charset=UTF-8"] s.require_path = "lib" + s.required_ruby_version = '>= 1.8.7' + s.add_development_dependency 'rake', '~> 10.0.0' s.add_development_dependency 'cucumber', '~> 1.1.9' s.add_development_dependency 'aruba', '~> 0.5' From 6bf124ee3b21871688e20e04eba5fcd6a314b270 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 14 Jun 2013 22:09:02 +1000 Subject: [PATCH 0044/1179] we cant require simplecov on 187 --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6717b97fa..470aa3a30 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,7 +5,7 @@ end unless ENV['NO_COVERALLS'] - require 'simplecov' + require 'simplecov' if RUBY_VERSION.to_f > 1.8 require 'coveralls' Coveralls.wear! do add_filter '/bundle/' From e3929590dde98a1e53aba70afc8f0783036124fb Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 14 Jun 2013 22:20:32 +1000 Subject: [PATCH 0045/1179] dont attempt to coverage 192 as it breaks --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 470aa3a30..d160f68a6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,7 +4,7 @@ rescue LoadError end -unless ENV['NO_COVERALLS'] +unless ENV['NO_COVERALLS'] || RUBY_VERSION == '1.9.2' require 'simplecov' if RUBY_VERSION.to_f > 1.8 require 'coveralls' Coveralls.wear! do From 68a4a1c4302c16a4eb1efb34674f52ab5ba3b69b Mon Sep 17 00:00:00 2001 From: Grant Hollingworth Date: Sat, 15 Jun 2013 11:10:04 -0600 Subject: [PATCH 0046/1179] remove reference to stub and should_receive from README --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 90f5ff4b2..8d2d3cd5d 100644 --- a/README.md +++ b/README.md @@ -145,9 +145,7 @@ person = double("person") expect(Person).to receive(:find) { person } ``` -We can do this with any object in a system because rspec-mocks adds the `stub` -and `should_receive` methods to every object, including class objects. When we -use either, RSpec replaces the method we're stubbing or mocking with its own +RSpec replaces the method we're stubbing or mocking with its own test-double-like method. At the end of the example, RSpec verifies any message expectations, and then restores the original methods. From f1bb620cfe651bdc37082bb8df1245665d3f4009 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 19 Jun 2013 13:43:51 +1000 Subject: [PATCH 0047/1179] correct comment typos --- spec/rspec/mocks/stub_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 50d25eb33..0d8b5ebc3 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -110,28 +110,28 @@ def existing_private_instance_method end it "restores existing instance methods" do - # See bug reports 8302 adn 7805 + # See bug reports 8302 and 7805 @instance.stub(:existing_instance_method) { :stub_value } reset @instance expect(@instance.existing_instance_method).to eq(:original_value) end it "restores existing private instance methods" do - # See bug reports 8302 adn 7805 + # See bug reports 8302 and 7805 @instance.stub(:existing_private_instance_method) { :stub_value } reset @instance expect(@instance.send(:existing_private_instance_method)).to eq(:original_value) end it "restores existing class methods" do - # See bug reports 8302 adn 7805 + # See bug reports 8302 and 7805 @class.stub(:existing_class_method) { :stub_value } reset @class expect(@class.existing_class_method).to eq(:original_value) end it "restores existing private class methods" do - # See bug reports 8302 adn 7805 + # See bug reports 8302 and 7805 @class.stub(:existing_private_class_method) { :stub_value } reset @class expect(@class.send(:existing_private_class_method)).to eq(:original_value) From be9af0da6aaa41793fc1736c4693b5d792f6ba16 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 19 Jun 2013 22:33:21 +1000 Subject: [PATCH 0048/1179] install bundler before travis run --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index db5172e3c..e33f390e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ +before_install: + - gem install bundler bundler_args: "--binstubs --standalone --without documentation" script: "script/test_all" rvm: From 571a470655c698081646fd1c94b8f6b57544049d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 20 Jun 2013 22:00:22 +1000 Subject: [PATCH 0049/1179] JRuby 1.7.4 in 1.9 mode is falsely reporting defined?(::BasicObject) => nil see jruby/jruby#814 --- lib/rspec/mocks/syntax.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index e8b6053f0..3db45fbcc 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -158,7 +158,7 @@ def self.default_should_syntax_host # On 1.8.7, Object.ancestors.last == Kernel but # things blow up if we include `RSpec::Mocks::Methods` # into Kernel...not sure why. - return Object unless defined?(::BasicObject) + return Object unless defined?(::BasicObject) || (RUBY_PLATFORM == 'java' && RUBY_VERSION.to_f > 1.8) # MacRuby has BasicObject but it's not the root class. return Object unless Object.ancestors.last == ::BasicObject From 727a9a4825bd39cc29081db735b7cede2bb00ab2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 5 Mar 2013 19:29:47 -0800 Subject: [PATCH 0050/1179] Refactor implementation pieces in prep for #230. This splits each implementation piece into a separate object so that it is easier to combine them. --- lib/rspec/mocks/message_expectation.rb | 79 +++++++++++++++++--------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 5d5dfaf18..95b785d6f 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -25,7 +25,9 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @failed_fast = nil @eval_context = nil @implementation = implementation - @values_to_return = nil + + @initial_implementation_logic = nil + @terminal_implementation_logic = nil end # @private @@ -78,8 +80,7 @@ def and_return(*values, &implementation) # TODO: deprecate `and_return { value }` @implementation = implementation else - @values_to_return = values - @implementation = build_implementation + self.terminal_implementation_logic = AndReturnImplementation.new(values) end end @@ -154,7 +155,7 @@ def and_throw(*args) def and_yield(*args, &block) yield @eval_context = Object.new if block @args_to_yield << args - @implementation = build_implementation + self.initial_implementation_logic = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator) self end @@ -418,7 +419,7 @@ def increase_actual_received_count! @actual_received_count += 1 end - protected + private def failed_fast? @failed_fast @@ -435,12 +436,20 @@ def set_expected_received_count(relativity, n) end end - private + def initial_implementation_logic=(logic) + @initial_implementation_logic = logic + update_implementation + end - def build_implementation - Implementation.new( - @values_to_return, @args_to_yield, - @eval_context, @error_generator + def terminal_implementation_logic=(logic) + @terminal_implementation_logic = logic + update_implementation + end + + def update_implementation + @implementation = Implementation.new( + @initial_implementation_logic, + @terminal_implementation_logic ).method(:call) end end @@ -462,29 +471,16 @@ def negative_expectation_for?(message) end end - # Represents a configured implementation. Takes into account - # `and_return` and `and_yield` instructions. + # Handles the implementation of an `and_yield` declaration. # @private - class Implementation - def initialize(values_to_return, args_to_yield, eval_context, error_generator) - @values_to_return = values_to_return + class AndYieldImplementation + def initialize(args_to_yield, eval_context, error_generator) @args_to_yield = args_to_yield @eval_context = eval_context @error_generator = error_generator end - def call(*args_to_ignore, &block) - default_return_value = perform_yield(&block) - return default_return_value unless @values_to_return - - if @values_to_return.size > 1 - @values_to_return.shift - else - @values_to_return.first - end - end - - def perform_yield(&block) + def call(*args_to_ignore, block) return if @args_to_yield.empty? && @eval_context.nil? @error_generator.raise_missing_block_error @args_to_yield unless block @@ -498,5 +494,34 @@ def perform_yield(&block) value end end + + # Handles the implementation of an `and_return` implementation. + # @private + class AndReturnImplementation + def initialize(values_to_return) + @values_to_return = values_to_return + end + + def call(*args_to_ignore, block) + if @values_to_return.size > 1 + @values_to_return.shift + else + @values_to_return.first + end + end + end + + # Represents a configured implementation. Takes into account + # any number of sub-implementations. + # @private + class Implementation + def initialize(*implementations) + @implementations = implementations.compact + end + + def call(*args, &block) + @implementations.map { |i| i.call(*args, block) }.last + end + end end end From f41cfcb76371b5faa8f2d931d3d7fa22fd98d027 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 11 Mar 2013 23:32:20 -0700 Subject: [PATCH 0051/1179] Clarify relationship between different method stub actions. In 2.13.0 only `and_yield` and `and_return` could be combined, since that was the only combination case that was specified by an example. This was a regression, as reported by a user in #230. I tried here to fully specify all of the various combinations of stub actions. Notes: * `and_return`, `and_raise` and `and_throw` are "terminal" actions in the sense that they terminate the method. They _must_ happen last and it is impossible to support more than one of these. Hence, we allow only one of these, and allow them to be overridden. We also return `nil` from these methods to discourage further stub configuration. * `and_call_original` is a special case that doesn't make sense to be combined with any of the others. Once you've set it up, this causes any further instructions to raise an error. * `and_yield` is treated as an "initial" action. Yielding doesn't exit a method the way the terminal actions do. It is the only initial action. Calling it multiple times sets up multiple yields. * Setting a block implementation (possible by passing a block to almost any method on the fluent interface) sets the block as the "inner" action. It runs between the configured yields (if there are any) and the configured terminal action (if there is one). My thinking here is that in many cases, users use a block implementation to specify a return value, essentially making it like a terminal action (so it should come after the `and_yield` actions), but in other cases, the user may just use a block for a side effect, and may configure an terminal action as well. Only one block implementation is supported and it can be overridden. --- Changelog.md | 4 + lib/rspec/mocks/message_expectation.rb | 119 +++++++---- ...bining_implementation_instructions_spec.rb | 197 ++++++++++++++++++ 3 files changed, 283 insertions(+), 37 deletions(-) create mode 100644 spec/rspec/mocks/combining_implementation_instructions_spec.rb diff --git a/Changelog.md b/Changelog.md index 300020a45..74b73e86c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,10 @@ Enhancements: Bug Fixes: * Bypass RSpec::Mocks::Syntax when mass-assigning stubs via double(). (Paul Annesley) +* Allow a block implementation to be used in combination with + `and_yield`, `and_raise`, `and_return` or `and_throw`. This got fixed + in 2.13.1 but failed to get merged into master for the 2.14.0.rc1 + release (Myron Marston). ### 2.14.0.rc1 / 2013-05-27 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 95b785d6f..f28ce6dcb 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -10,7 +10,7 @@ class MessageExpectation # @private def initialize(error_generator, expectation_ordering, expected_from, method_double, - expected_received_count=1, opts={}, &implementation) + expected_received_count=1, opts={}, &implementation_block) @error_generator = error_generator @error_generator.opts = opts @expected_from = expected_from @@ -24,10 +24,9 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @args_to_yield = [] @failed_fast = nil @eval_context = nil - @implementation = implementation - @initial_implementation_logic = nil - @terminal_implementation_logic = nil + @implementation = Implementation.new + self.inner_implementation_action = implementation_block end # @private @@ -78,10 +77,12 @@ def and_return(*values, &implementation) if implementation # TODO: deprecate `and_return { value }` - @implementation = implementation + self.inner_implementation_action = implementation else - self.terminal_implementation_logic = AndReturnImplementation.new(values) + self.terminal_implementation_action = AndReturnImplementation.new(values) end + + nil end # Tells the object to delegate to the original unmodified method @@ -99,7 +100,7 @@ def and_call_original if @method_double.object.is_a?(RSpec::Mocks::TestDouble) @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original) else - @implementation = @method_double.original_method + @implementation = AndCallOriginalImplementation.new(@method_double.original_method) end end @@ -129,7 +130,8 @@ def and_raise(exception = RuntimeError, message = nil) exception = message ? exception.exception(message) : exception.exception end - @implementation = Proc.new { raise exception } + self.terminal_implementation_action = Proc.new { raise exception } + nil end # @overload and_throw(symbol) @@ -143,7 +145,8 @@ def and_raise(exception = RuntimeError, message = nil) # car.stub(:go).and_throw(:out_of_gas) # car.stub(:go).and_throw(:out_of_gas, :level => 0.1) def and_throw(*args) - @implementation = Proc.new { throw(*args) } + self.terminal_implementation_action = Proc.new { throw(*args) } + nil end # Tells the object to yield one or more args to a block when the message @@ -155,7 +158,7 @@ def and_throw(*args) def and_yield(*args, &block) yield @eval_context = Object.new if block @args_to_yield << args - self.initial_implementation_logic = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator) + self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator) self end @@ -177,8 +180,8 @@ def invoke(parent_stub, *args, &block) @order_group.handle_order_constraint self begin - if @implementation - @implementation.call(*args, &block) + if implementation.present? + implementation.call(*args, &block) elsif parent_stub parent_stub.invoke(nil, *args, &block) end @@ -292,7 +295,7 @@ def raise_out_of_order_error # cart.add(Book.new(:isbn => 1934356379)) # # => passes def with(*args, &block) - @implementation = block if block_given? unless args.empty? + self.inner_implementation_action = block if block_given? unless args.empty? @argument_list_matcher = ArgumentListMatcher.new(*args, &block) self end @@ -304,7 +307,7 @@ def with(*args, &block) # # dealer.should_receive(:deal_card).exactly(10).times def exactly(n, &block) - @implementation = block if block + self.inner_implementation_action = block set_expected_received_count :exactly, n self end @@ -320,7 +323,7 @@ def at_least(n, &block) RSpec.deprecate "at_least(0) with should_receive", :replacement => "stub" end - @implementation = block if block + self.inner_implementation_action = block set_expected_received_count :at_least, n self end @@ -332,7 +335,7 @@ def at_least(n, &block) # # dealer.should_receive(:deal_card).at_most(10).times def at_most(n, &block) - @implementation = block if block + self.inner_implementation_action = block set_expected_received_count :at_most, n self end @@ -345,7 +348,7 @@ def at_most(n, &block) # dealer.should_receive(:deal_card).at_least(10).times # dealer.should_receive(:deal_card).at_most(10).times def times(&block) - @implementation = block if block + self.inner_implementation_action = block self end @@ -353,7 +356,7 @@ def times(&block) # Allows an expected message to be received any number of times. def any_number_of_times(&block) RSpec.deprecate "any_number_of_times", :replacement => "stub" - @implementation = block if block + self.inner_implementation_action = block @expected_received_count = :any self end @@ -374,7 +377,7 @@ def never # # car.should_receive(:go).once def once(&block) - @implementation = block if block + self.inner_implementation_action = block set_expected_received_count :exactly, 1 self end @@ -385,7 +388,7 @@ def once(&block) # # car.should_receive(:go).twice def twice(&block) - @implementation = block if block + self.inner_implementation_action = block set_expected_received_count :exactly, 2 self end @@ -398,7 +401,7 @@ def twice(&block) # api.should_receive(:run).ordered # api.should_receive(:finish).ordered def ordered(&block) - @implementation = block if block + self.inner_implementation_action = block @order_group.register(self) @ordered = true self @@ -436,21 +439,16 @@ def set_expected_received_count(relativity, n) end end - def initial_implementation_logic=(logic) - @initial_implementation_logic = logic - update_implementation + def initial_implementation_action=(action) + implementation.initial_action = action end - def terminal_implementation_logic=(logic) - @terminal_implementation_logic = logic - update_implementation + def inner_implementation_action=(action) + implementation.inner_action = action if action end - def update_implementation - @implementation = Implementation.new( - @initial_implementation_logic, - @terminal_implementation_logic - ).method(:call) + def terminal_implementation_action=(action) + implementation.terminal_action = action end end @@ -480,7 +478,7 @@ def initialize(args_to_yield, eval_context, error_generator) @error_generator = error_generator end - def call(*args_to_ignore, block) + def call(*args_to_ignore, &block) return if @args_to_yield.empty? && @eval_context.nil? @error_generator.raise_missing_block_error @args_to_yield unless block @@ -502,7 +500,7 @@ def initialize(values_to_return) @values_to_return = values_to_return end - def call(*args_to_ignore, block) + def call(*args_to_ignore, &block) if @values_to_return.size > 1 @values_to_return.shift else @@ -515,12 +513,59 @@ def call(*args_to_ignore, block) # any number of sub-implementations. # @private class Implementation - def initialize(*implementations) - @implementations = implementations.compact + attr_accessor :initial_action, :inner_action, :terminal_action + + def call(*args, &block) + actions.map do |action| + action.call(*args, &block) + end.last + end + + def present? + actions.any? + end + + private + + def actions + [initial_action, inner_action, terminal_action].compact + end + end + + # Represents an `and_call_original` implementation. + # @private + class AndCallOriginalImplementation + def initialize(method) + @method = method + end + + CannotModifyFurtherError = Class.new(StandardError) + + def initial_action=(value) + raise cannot_modify_further_error + end + + def inner_action=(value) + raise cannot_modify_further_error + end + + def terminal_action=(value) + raise cannot_modify_further_error + end + + def present? + true end def call(*args, &block) - @implementations.map { |i| i.call(*args, block) }.last + @method.call(*args, &block) + end + + private + + def cannot_modify_further_error + CannotModifyFurtherError.new "This method has already been configured " + + "to call the original implementation, and cannot be modified further." end end end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb new file mode 100644 index 000000000..7204459c9 --- /dev/null +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -0,0 +1,197 @@ +require 'spec_helper' + +module RSpec + module Mocks + describe "Combining implementation instructions" do + it 'can combine and_yield and and_return' do + dbl = double + dbl.stub(:foo).and_yield(5).and_return(3) + + expect { |b| + expect(dbl.foo(&b)).to eq(3) + }.to yield_with_args(5) + end + + describe "combining and_yield, a block implementation and and_return" do + def verify_combined_implementation + dbl = double + (yield dbl).and_yield(5).and_return(3) + + expect { |b| + expect(dbl.foo(:arg, &b)).to eq(3) + }.to yield_with_args(5) + + expect(@block_called).to be_true + end + + it 'works when passing a block to `stub`' do + verify_combined_implementation do |dbl| + dbl.stub(:foo) { @block_called = true } + end + end + + it 'works when passing a block to `with`' do + verify_combined_implementation do |dbl| + dbl.stub(:foo).with(:arg) { @block_called = true } + end + end + + it 'works when passing a block to `exactly`' do + verify_combined_implementation do |dbl| + dbl.should_receive(:foo).exactly(:once) { @block_called = true } + end + end + + it 'works when passing a block to `at_least`' do + verify_combined_implementation do |dbl| + dbl.should_receive(:foo).at_least(:once) { @block_called = true } + end + end + + it 'works when passing a block to `at_most`' do + verify_combined_implementation do |dbl| + dbl.should_receive(:foo).at_most(:once) { @block_called = true } + end + end + + it 'works when passing a block to `times`' do + verify_combined_implementation do |dbl| + dbl.should_receive(:foo).exactly(1).times { @block_called = true } + end + end + + it 'works when passing a block to `any_number_of_times`' do + verify_combined_implementation do |dbl| + dbl.should_receive(:foo).any_number_of_times { @block_called = true } + end + end + + it 'works when passing a block to `once`' do + verify_combined_implementation do |dbl| + dbl.should_receive(:foo).once { @block_called = true } + end + end + + it 'works when passing a block to `twice`' do + the_double = nil + + verify_combined_implementation do |dbl| + the_double = dbl + dbl.should_receive(:foo).twice { @block_called = true } + end + + the_double.foo { |a| } # to ensure it is called twice + end + + it 'works when passing a block to `ordered`' do + verify_combined_implementation do |dbl| + dbl.should_receive(:foo).ordered { @block_called = true } + end + end + end + + it 'can combine and_yield and and_return with a block' do + dbl = double + dbl.stub(:foo).and_yield(5).and_return { :return } + + expect { |b| + expect(dbl.foo(&b)).to eq(:return) + }.to yield_with_args(5) + end + + it 'can combine and_yield and and_raise' do + dbl = double + dbl.stub(:foo).and_yield(5).and_raise("boom") + + expect { |b| + expect { dbl.foo(&b) }.to raise_error("boom") + }.to yield_with_args(5) + end + + it 'can combine and_yield, a block implementation and and_raise' do + dbl = double + block_called = false + dbl.stub(:foo) { block_called = true }.and_yield(5).and_raise("boom") + + expect { |b| + expect { dbl.foo(&b) }.to raise_error("boom") + }.to yield_with_args(5) + + expect(block_called).to be_true + end + + it 'can combine and_yield and and_throw' do + dbl = double + dbl.stub(:foo).and_yield(5).and_throw(:bar) + + expect { |b| + expect { dbl.foo(&b) }.to throw_symbol(:bar) + }.to yield_with_args(5) + end + + it 'can combine and_yield, a block implementation and and_throw' do + dbl = double + block_called = false + dbl.stub(:foo) { block_called = true }.and_yield(5).and_throw(:bar) + + expect { |b| + expect { dbl.foo(&b) }.to throw_symbol(:bar) + }.to yield_with_args(5) + + expect(block_called).to be_true + end + + it 'returns `nil` from all terminal actions to discourage further configuration' do + expect(double.stub(:foo).and_return(1)).to be_nil + expect(double.stub(:foo).and_raise("boom")).to be_nil + expect(double.stub(:foo).and_throw(:foo)).to be_nil + end + + it 'allows the terminal action to be overriden' do + dbl = double + stubbed_double = dbl.stub(:foo) + + stubbed_double.and_return(1) + expect(dbl.foo).to eq(1) + + stubbed_double.and_return(3) + expect(dbl.foo).to eq(3) + + stubbed_double.and_raise("boom") + expect { dbl.foo }.to raise_error("boom") + + stubbed_double.and_throw(:bar) + expect { dbl.foo }.to throw_symbol(:bar) + end + + it 'allows the inner implementation block to be overriden' do + dbl = double + stubbed_double = dbl.stub(:foo) + + stubbed_double.with(:arg) { :with_block } + expect(dbl.foo(:arg)).to eq(:with_block) + + stubbed_double.at_least(:once) { :at_least_block } + expect(dbl.foo(:arg)).to eq(:at_least_block) + end + + it 'raises an error if `and_call_original` is followed by any other instructions' do + dbl = [1, 2, 3] + stubbed = dbl.stub(:size) + stubbed.and_call_original + + msg_fragment = /cannot be modified further/ + + expect { stubbed.and_yield }.to raise_error(msg_fragment) + expect { stubbed.and_return(1) }.to raise_error(msg_fragment) + expect { stubbed.and_raise("a") }.to raise_error(msg_fragment) + expect { stubbed.and_throw(:bar) }.to raise_error(msg_fragment) + + expect { stubbed.once { } }.to raise_error(msg_fragment) + + expect(dbl.size).to eq(3) + end + end + end +end + From 803c336f96de5a504a68ef322b7a61e910244a16 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 21 Jun 2013 09:16:33 +1000 Subject: [PATCH 0052/1179] Refactor JRuby fix with more useful message --- lib/rspec/mocks/syntax.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 3db45fbcc..e86885686 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -155,10 +155,17 @@ def self.expect_enabled?(syntax_host = ::RSpec::Mocks::ExampleMethods) # @api private # Determines where the methods like `should_receive`, and `stub` are added. def self.default_should_syntax_host + # JRuby 1.7.4 introduces a regression whereby `defined?(::BasicObject) => nil` + # yet `BasicObject` still exists and patching onto ::Object breaks things + # e.g. SimpleDelegator expectations won't work see jruby/jruby#814 + if defined?(JRUBY_VERSION) && JRUBY_VERSION == '1.7.4' && RUBY_VERSION.to_f > 1.8 + return ::BasicObject + end + # On 1.8.7, Object.ancestors.last == Kernel but # things blow up if we include `RSpec::Mocks::Methods` # into Kernel...not sure why. - return Object unless defined?(::BasicObject) || (RUBY_PLATFORM == 'java' && RUBY_VERSION.to_f > 1.8) + return Object unless defined?(::BasicObject) # MacRuby has BasicObject but it's not the root class. return Object unless Object.ancestors.last == ::BasicObject From a457df08cbb0516443eba36020c0cb1d254e80ff Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 21 Jun 2013 15:11:15 +1000 Subject: [PATCH 0053/1179] expand gh ref to full url --- lib/rspec/mocks/syntax.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index e86885686..5bffa431c 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -157,7 +157,9 @@ def self.expect_enabled?(syntax_host = ::RSpec::Mocks::ExampleMethods) def self.default_should_syntax_host # JRuby 1.7.4 introduces a regression whereby `defined?(::BasicObject) => nil` # yet `BasicObject` still exists and patching onto ::Object breaks things - # e.g. SimpleDelegator expectations won't work see jruby/jruby#814 + # e.g. SimpleDelegator expectations won't work + # + # See: https://github.com/jruby/jruby/issues/814 if defined?(JRUBY_VERSION) && JRUBY_VERSION == '1.7.4' && RUBY_VERSION.to_f > 1.8 return ::BasicObject end From ceb79d6d6af1a25708cf4101446f08aa74fd8b4d Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Sat, 22 Jun 2013 02:12:13 +1000 Subject: [PATCH 0054/1179] RSpec::Mocks.allow_message --- lib/rspec/mocks.rb | 5 +++ .../syntax_agnostic_message_matchers_spec.rb | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 1fa5bc303..836399440 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -30,6 +30,11 @@ def any_instance_recorder_for(klass) space.any_instance_recorder_for(klass) end + def allow_message(subject, message, &block) + ::RSpec::Mocks.proxy_for(subject). + add_stub(caller(1)[0], message.to_sym, &block) + end + # @api private KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb new file mode 100644 index 000000000..f98aa32e7 --- /dev/null +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -0,0 +1,41 @@ +require "spec_helper" + +module RSpec + module Mocks + + describe ".allow_message" do + let(:subject) { Object.new } + + it "sets up basic message allowance" do + expect { + ::RSpec::Mocks.allow_message(subject, :basic) + }.to change { + subject.respond_to?(:basic) + }.to(true) + + expect(subject.basic).to eq(nil) + end + + it "sets up message allowance with params and return value" do + expect { + ::RSpec::Mocks.allow_message(subject, :x).with(:in).and_return(:out) + }.to change { + subject.respond_to?(:x) + }.to(true) + + expect(subject.x(:in)).to eq(:out) + end + + it "accepts a callable responder for the message" do + ::RSpec::Mocks.allow_message(subject, :message) { :value } + expect(subject.message).to eq(:value) + end + + end + + describe ".expect_message" do + pending + end + + end +end From 802413297e90d2b47457553957e7d75e937406a4 Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Sat, 22 Jun 2013 14:15:25 +1000 Subject: [PATCH 0055/1179] RSpec::Mocks.expect_message() --- lib/rspec/mocks.rb | 5 +++ .../syntax_agnostic_message_matchers_spec.rb | 31 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 836399440..d89d9eb9d 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -35,6 +35,11 @@ def allow_message(subject, message, &block) add_stub(caller(1)[0], message.to_sym, &block) end + def expect_message(subject, message, &block) + ::RSpec::Mocks.proxy_for(subject). + add_message_expectation(caller(1)[0], message.to_sym, &block) + end + # @api private KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index f98aa32e7..8a99947ac 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -34,7 +34,36 @@ module Mocks end describe ".expect_message" do - pending + let(:subject) { Object.new } + + it "sets up basic message expectation, verifies as uncalled" do + expect { + ::RSpec::Mocks.expect_message(subject, :basic) + }.to change { + subject.respond_to?(:basic) + }.to(true) + + expect { verify subject }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "sets up basic message expectation, verifies as called" do + ::RSpec::Mocks.expect_message(subject, :basic) + subject.basic + verify subject + end + + it "sets up message expectation with params and return value" do + ::RSpec::Mocks.expect_message(subject, :msg).with(:in).and_return(:out) + expect(subject.msg(:in)).to eq(:out) + verify subject + end + + it "accepts a block implementation for the expected message" do + ::RSpec::Mocks.expect_message(subject, :msg) { :value } + expect(subject.msg).to eq(:value) + verify subject + end + end end From e5712ef851d54e06f7dc5a074efed69b931f2f80 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 22 Jun 2013 17:34:34 +1000 Subject: [PATCH 0056/1179] document expect_any_instance_of and allow_any_instance_of --- .../expect_any_instance_of.feature | 49 +++++++++++++++++++ .../expect_message_using_expect.feature | 3 ++ 2 files changed, 52 insertions(+) create mode 100644 features/message_expectations/expect_any_instance_of.feature diff --git a/features/message_expectations/expect_any_instance_of.feature b/features/message_expectations/expect_any_instance_of.feature new file mode 100644 index 000000000..7b2c8cfee --- /dev/null +++ b/features/message_expectations/expect_any_instance_of.feature @@ -0,0 +1,49 @@ +Feature: expect/allow a message on any instance of a class + + Use `expect_any_instance_of(Class).to receive` to set an expectation that one + (and only one) instance of a class receives a message before the example is + completed. The spec will fail if no instance receives a message. + + Use `allow_any_instance_of(Class).to receive` to allow any instance of a class + to receive that message. This won't fail if no instance receives that message. + + Scenario: expect a message on any instance of a class + Given a file named "example_spec.rb" with: + """ruby + describe "expect_any_instance_of" do + before do + expect_any_instance_of(Object).to receive(:foo).and_return(:return_value) + end + + it "verifies that one instance of the class receives the message" do + o = Object.new + expect(o.foo).to eq(:return_value) + end + + it "fails if no instance receives that message" do + o = Object.new + end + end + """ + When I run `rspec example_spec.rb` + Then the output should contain "2 examples, 1 failure" + + Scenario: allowing a message on any instance of a class + Given a file named "example_spec.rb" with: + """ruby + describe "any_instance.should_receive" do + before do + allow_any_instance_of(Object).to receive(:foo).and_return(:return_value) + end + + it "allows any instance of the class to receive the message" do + o = Object.new + expect(o.foo).to eq(:return_value) + end + it "wont fail if no instances receive that message" do + o = Object.new + end + end + """ + When I run `rspec example_spec.rb` + Then the examples should all pass diff --git a/features/message_expectations/expect_message_using_expect.feature b/features/message_expectations/expect_message_using_expect.feature index c67bca9ee..a205b4c5d 100644 --- a/features/message_expectations/expect_message_using_expect.feature +++ b/features/message_expectations/expect_message_using_expect.feature @@ -4,6 +4,9 @@ Feature: expect message using `expect` `object` should receive the message `:message` before the example is completed. + Note: You should use `expect_any_instance_of` when working with any + instance of a class, see message_expectations/expect_any_instance + Scenario: expect a message Given a file named "spec/account_spec.rb" with: """ruby From b0a53540f645ddb4e82951e7d69eb2ada4bfebc0 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 22 Jun 2013 19:29:36 +1000 Subject: [PATCH 0057/1179] fix issue where you couldn't use and_call_original on any_instance stuff --- lib/rspec/mocks/any_instance/stub_chain.rb | 3 ++- spec/rspec/mocks/any_instance_spec.rb | 12 ++++++++++++ .../combining_implementation_instructions_spec.rb | 8 ++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index d4c4ee905..9b1712a70 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -22,7 +22,8 @@ def invocation_order :with => [nil], :and_return => [:with, nil], :and_raise => [:with, nil], - :and_yield => [:with, nil] + :and_yield => [:with, nil], + :and_call_original => [:with, nil] } end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 8bba07c5f..b7e873e7e 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -181,6 +181,18 @@ def private_method; :private_method_return_value; end end end + context 'with #and_call_original and competing #with' do + let(:klass) { Struct.new(:a_method) } + + it 'can combine and_call_original, with, and_return' do + allow_any_instance_of(klass).to receive(:a_method).and_call_original + allow_any_instance_of(klass).to receive(:a_method).with(:arg).and_return('value') + + expect(klass.new('org').a_method).to eq 'org' + expect(klass.new.a_method(:arg)).to eq 'value' + end + end + context "with #and_raise" do it "stubs a method that doesn't exist" do klass.any_instance.stub(:foo).and_raise(CustomErrorForAnyInstanceSpec) diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 7204459c9..64c3450ad 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -175,6 +175,14 @@ def verify_combined_implementation expect(dbl.foo(:arg)).to eq(:at_least_block) end + it 'can combine and_call_original, with, and_return' do + obj = Struct.new(:value).new('original') + obj.stub(:value).and_call_original + obj.stub(:value).with(:arg).and_return('value') + expect(obj.value).to eq 'original' + expect(obj.value(:arg)).to eq 'value' + end + it 'raises an error if `and_call_original` is followed by any other instructions' do dbl = [1, 2, 3] stubbed = dbl.stub(:size) From d93eae5d203eacfa36de2bbd59518dd1040725d5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 23 Jun 2013 08:12:55 +1000 Subject: [PATCH 0058/1179] assert upon old syntax too --- spec/rspec/mocks/any_instance_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index b7e873e7e..f9522d403 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -191,6 +191,14 @@ def private_method; :private_method_return_value; end expect(klass.new('org').a_method).to eq 'org' expect(klass.new.a_method(:arg)).to eq 'value' end + + it 'can combine and_call_original, with, and_return (old syntax)' do + klass.any_instance.stub(:a_method).and_call_original + klass.any_instance.stub(:a_method).with(:arg).and_return('value') + + expect(klass.new('org').a_method).to eq 'org' + expect(klass.new.a_method(:arg)).to eq 'value' + end end context "with #and_raise" do From f36809160711d34b42fed64c4e82cca3eba282d3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 24 Jun 2013 12:23:32 +1000 Subject: [PATCH 0059/1179] ws --- features/message_expectations/expect_any_instance_of.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/message_expectations/expect_any_instance_of.feature b/features/message_expectations/expect_any_instance_of.feature index 7b2c8cfee..a5968c484 100644 --- a/features/message_expectations/expect_any_instance_of.feature +++ b/features/message_expectations/expect_any_instance_of.feature @@ -40,6 +40,7 @@ Feature: expect/allow a message on any instance of a class o = Object.new expect(o.foo).to eq(:return_value) end + it "wont fail if no instances receive that message" do o = Object.new end From 3f2925c5c6371703e8fd2f3cb9beb436aefa1d39 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 24 Jun 2013 12:28:50 +1000 Subject: [PATCH 0060/1179] reworded description --- features/message_expectations/expect_any_instance_of.feature | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/message_expectations/expect_any_instance_of.feature b/features/message_expectations/expect_any_instance_of.feature index a5968c484..63b81db00 100644 --- a/features/message_expectations/expect_any_instance_of.feature +++ b/features/message_expectations/expect_any_instance_of.feature @@ -4,8 +4,9 @@ Feature: expect/allow a message on any instance of a class (and only one) instance of a class receives a message before the example is completed. The spec will fail if no instance receives a message. - Use `allow_any_instance_of(Class).to receive` to allow any instance of a class - to receive that message. This won't fail if no instance receives that message. + Use `allow_any_instance_of(Class).to receive` when an instance of a class may + respond to a particular message. This will not set an expectation on any instance + so the spec will not fail if no instance receives the message. Scenario: expect a message on any instance of a class Given a file named "example_spec.rb" with: From e6675c5519f727dc7d4fc2f94386af40528e7750 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 24 Jun 2013 12:43:10 +1000 Subject: [PATCH 0061/1179] assert on which spec is failing --- features/message_expectations/expect_any_instance_of.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/message_expectations/expect_any_instance_of.feature b/features/message_expectations/expect_any_instance_of.feature index 63b81db00..1b2802e02 100644 --- a/features/message_expectations/expect_any_instance_of.feature +++ b/features/message_expectations/expect_any_instance_of.feature @@ -28,6 +28,7 @@ Feature: expect/allow a message on any instance of a class """ When I run `rspec example_spec.rb` Then the output should contain "2 examples, 1 failure" + And the output should contain "1) expect_any_instance_of fails if no instance receives that message" Scenario: allowing a message on any instance of a class Given a file named "example_spec.rb" with: From 906aaf86d2c82eef5d2278484896148079024df8 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 24 Jun 2013 12:43:22 +1000 Subject: [PATCH 0062/1179] change note about features --- .../message_expectations/expect_message_using_expect.feature | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/message_expectations/expect_message_using_expect.feature b/features/message_expectations/expect_message_using_expect.feature index a205b4c5d..3a86ec6ab 100644 --- a/features/message_expectations/expect_message_using_expect.feature +++ b/features/message_expectations/expect_message_using_expect.feature @@ -4,8 +4,9 @@ Feature: expect message using `expect` `object` should receive the message `:message` before the example is completed. - Note: You should use `expect_any_instance_of` when working with any - instance of a class, see message_expectations/expect_any_instance + Note: You can use `expect_any_instance_of` when you don't have a reference + to the object that receives a message in your test. For more information, + see the message_expectations/expect_any_instance_of feature. Scenario: expect a message Given a file named "spec/account_spec.rb" with: From bbb0502b1f00afea9348fd70d279ac1398ac5f86 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 26 Jun 2013 09:02:35 +1000 Subject: [PATCH 0063/1179] documentation for allow --- .../allow_any_instance_of.feature | 26 +++++++++++ .../expect_any_instance_of.feature | 27 +----------- features/method_stubs/README.md | 28 +++++++++++- features/method_stubs/any_instance.feature | 6 ++- features/method_stubs/as_null_object.feature | 7 ++- .../simple_return_value_with_allow.feature | 44 +++++++++++++++++++ ... => simple_return_value_with_stub.feature} | 0 .../method_stubs/stub_implementation.feature | 24 +++++++++- 8 files changed, 132 insertions(+), 30 deletions(-) create mode 100644 features/message_expectations/allow_any_instance_of.feature create mode 100644 features/method_stubs/simple_return_value_with_allow.feature rename features/method_stubs/{simple_return_value.feature => simple_return_value_with_stub.feature} (100%) diff --git a/features/message_expectations/allow_any_instance_of.feature b/features/message_expectations/allow_any_instance_of.feature new file mode 100644 index 000000000..e8247c401 --- /dev/null +++ b/features/message_expectations/allow_any_instance_of.feature @@ -0,0 +1,26 @@ +Feature: allow a message on any instance of a class + + Use `allow_any_instance_of(Class).to receive` when an instance of a class may + respond to a particular message. This will not set an expectation on any instance + so the spec will not fail if no instance receives the message. + + Scenario: allowing a message on any instance of a class + Given a file named "example_spec.rb" with: + """ruby + describe "any_instance.should_receive" do + before do + allow_any_instance_of(Object).to receive(:foo).and_return(:return_value) + end + + it "allows any instance of the class to receive the message" do + o = Object.new + expect(o.foo).to eq(:return_value) + end + + it "wont fail if no instances receive that message" do + o = Object.new + end + end + """ + When I run `rspec example_spec.rb` + Then the examples should all pass diff --git a/features/message_expectations/expect_any_instance_of.feature b/features/message_expectations/expect_any_instance_of.feature index 1b2802e02..c15fe901b 100644 --- a/features/message_expectations/expect_any_instance_of.feature +++ b/features/message_expectations/expect_any_instance_of.feature @@ -1,13 +1,9 @@ -Feature: expect/allow a message on any instance of a class +Feature: expect a message on any instance of a class Use `expect_any_instance_of(Class).to receive` to set an expectation that one (and only one) instance of a class receives a message before the example is completed. The spec will fail if no instance receives a message. - Use `allow_any_instance_of(Class).to receive` when an instance of a class may - respond to a particular message. This will not set an expectation on any instance - so the spec will not fail if no instance receives the message. - Scenario: expect a message on any instance of a class Given a file named "example_spec.rb" with: """ruby @@ -29,24 +25,3 @@ Feature: expect/allow a message on any instance of a class When I run `rspec example_spec.rb` Then the output should contain "2 examples, 1 failure" And the output should contain "1) expect_any_instance_of fails if no instance receives that message" - - Scenario: allowing a message on any instance of a class - Given a file named "example_spec.rb" with: - """ruby - describe "any_instance.should_receive" do - before do - allow_any_instance_of(Object).to receive(:foo).and_return(:return_value) - end - - it "allows any instance of the class to receive the message" do - o = Object.new - expect(o.foo).to eq(:return_value) - end - - it "wont fail if no instances receive that message" do - o = Object.new - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass diff --git a/features/method_stubs/README.md b/features/method_stubs/README.md index 014b2fc5e..9bfea21ae 100644 --- a/features/method_stubs/README.md +++ b/features/method_stubs/README.md @@ -3,7 +3,11 @@ # create a double obj = double() - # specify a return value + # specify a return value using `expect` syntax + allow(obj).to receive(:message) { :value } + allow(obj).to receive(:message).and_return(:value) + + # specify a return value using `should` syntax obj.stub(:message) { :value } obj.stub(:message => :value) obj.stub(:message).and_return(:value) @@ -14,6 +18,11 @@ block contents are evaluated lazily when the `obj` receives the ### Fake implementation + allow(obj).to receive(:message) do |arg1, arg2| + # set expectations about the args in this block + # and/or set a return value + end + obj.stub(:message) do |arg1, arg2| # set expectations about the args in this block # and/or set a return value @@ -21,11 +30,17 @@ block contents are evaluated lazily when the `obj` receives the ### Raising/Throwing + allow(obj).to receive(:message).and_raise("this error") + allow(obj).to receive(:message).and_throw(:this_symbol) + obj.stub(:message).and_raise("this error") obj.stub(:message).and_throw(:this_symbol) You can also use the block format: + allow(obj).to receive(:message) { raise "this error" } + allow(obj).to receive(:message) { throw :this_symbol } + obj.stub(:message) { raise "this error" } obj.stub(:message) { throw :this_symbol } @@ -33,11 +48,20 @@ You can also use the block format: #### Explicit arguments + allow(obj).to receive(:message).with('an argument') { ... } + obj.stub(:message).with('an argument') { ... } obj.stub(:message).with('more_than', 'one_argument') { ... } #### Argument matchers + allow(obj).to receive(:message).with(anything()) { ... } + allow(obj).to receive(:message).with(an_instance_of(Money)) { ... } + allow(obj).to receive(:message).with(hash_including(:a => 'b')) { ... } + allow(obj).to receive(:message).with(array_including(1,2,3)) { ... } + or + allow(obj).to receive(:message).with(array_including([1,2,3])) { ... } + obj.stub(:message).with(anything()) { ... } obj.stub(:message).with(an_instance_of(Money)) { ... } obj.stub(:message).with(hash_including(:a => 'b')) { ... } @@ -47,4 +71,6 @@ You can also use the block format: #### Regular expressions + allow(obj).to receive(:message).with(/abc/) { ... } + obj.stub(:message).with(/abc/) { ... } diff --git a/features/method_stubs/any_instance.feature b/features/method_stubs/any_instance.feature index bb7115baa..65e84eede 100644 --- a/features/method_stubs/any_instance.feature +++ b/features/method_stubs/any_instance.feature @@ -6,6 +6,10 @@ Feature: stub on any instance of a class Messages can be stubbed on any class, including those in Ruby's core library. + Note: You can use `allow_any_instance_of` when you don't have a reference + to the object that receives a message in your test. For more information, + see the message_expectations/allow_any_instance_of feature. + Scenario: any_instance stub with a single return value Given a file named "example_spec.rb" with: """ruby @@ -129,4 +133,4 @@ Feature: stub on any instance of a class end """ When I run `rspec stub_chain_spec.rb` - Then the examples should all pass \ No newline at end of file + Then the examples should all pass diff --git a/features/method_stubs/as_null_object.feature b/features/method_stubs/as_null_object.feature index b1ee3618c..afddf9e0c 100644 --- a/features/method_stubs/as_null_object.feature +++ b/features/method_stubs/as_null_object.feature @@ -16,7 +16,12 @@ Feature: as_null_object null_object.should respond_to(:an_undefined_method) end - it "allows explicit stubs" do + it "allows explicit stubs using expect syntax" do + allow(null_object).to receive(:foo) { "bar" } + expect(null_object.foo).to eq("bar") + end + + it "allows explicit stubs using should syntax" do null_object.stub(:foo) { "bar" } null_object.foo.should eq("bar") end diff --git a/features/method_stubs/simple_return_value_with_allow.feature b/features/method_stubs/simple_return_value_with_allow.feature new file mode 100644 index 000000000..82fca7500 --- /dev/null +++ b/features/method_stubs/simple_return_value_with_allow.feature @@ -0,0 +1,44 @@ +Feature: allow with a simple return value + + Use the `allow` method with the `receive` matcher on a test double or a real + object to tell the object to return a value (or values) in response to a given + message. Nothing happens if the message is never received. + + Scenario: stub with no return value + Given a file named "example_spec.rb" with: + """ruby + describe "a stub with no return value specified" do + let(:collaborator) { double("collaborator") } + + it "returns nil" do + allow(collaborator).to receive(:message) + expect(collaborator.message).to be(nil) + end + end + """ + When I run `rspec example_spec.rb` + Then the examples should all pass + + Scenario: stubs with return values + Given a file named "example_spec.rb" with: + """ruby + describe "a stub with a return value" do + context "specified in a block" do + it "returns the specified value" do + collaborator = double("collaborator") + allow(collaborator).to receive(:message) { :value } + expect(collaborator.message).to eq(:value) + end + end + + context "specified with #and_return" do + it "returns the specified value" do + collaborator = double("collaborator") + allow(collaborator).to receive(:message).and_return(:value) + expect(collaborator.message).to eq(:value) + end + end + end + """ + When I run `rspec example_spec.rb` + Then the examples should all pass diff --git a/features/method_stubs/simple_return_value.feature b/features/method_stubs/simple_return_value_with_stub.feature similarity index 100% rename from features/method_stubs/simple_return_value.feature rename to features/method_stubs/simple_return_value_with_stub.feature diff --git a/features/method_stubs/stub_implementation.feature b/features/method_stubs/stub_implementation.feature index 269129bd5..581e43dcb 100644 --- a/features/method_stubs/stub_implementation.feature +++ b/features/method_stubs/stub_implementation.feature @@ -3,7 +3,29 @@ Feature: stub with substitute implementation You can stub an implementation of a method (a.k.a. fake) by passing a block to the `stub` method. - Scenario: stub implementation + Scenario: stub implementation using `expect` syntax + Given a file named "stub_implementation_spec.rb" with: + """ruby + describe "a stubbed implementation" do + it "works" do + object = Object.new + allow(object).to receive(:foo) do |arg| + if arg == :this + "got this" + elsif arg == :that + "got that" + end + end + + expect(object.foo(:this)).to eq("got this") + expect(object.foo(:that)).to eq("got that") + end + end + """ + When I run `rspec stub_implementation_spec.rb` + Then the output should contain "1 example, 0 failures" + + Scenario: stub implementation using `should` syntax Given a file named "stub_implementation_spec.rb" with: """ruby describe "a stubbed implementation" do From 795c223f8b8884367d801ad367aa3514f6127276 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 26 Jun 2013 19:44:53 +1000 Subject: [PATCH 0064/1179] rewording the blurb on alloow and readme --- .../message_expectations/allow_any_instance_of.feature | 6 +++--- features/method_stubs/README.md | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/features/message_expectations/allow_any_instance_of.feature b/features/message_expectations/allow_any_instance_of.feature index e8247c401..9c1ab609d 100644 --- a/features/message_expectations/allow_any_instance_of.feature +++ b/features/message_expectations/allow_any_instance_of.feature @@ -1,8 +1,8 @@ Feature: allow a message on any instance of a class - Use `allow_any_instance_of(Class).to receive` when an instance of a class may - respond to a particular message. This will not set an expectation on any instance - so the spec will not fail if no instance receives the message. + Use `allow_any_instance_of(Class).to receive` when an instance of a class is + allowed to respond to a particular message. This will not set an expectation + on any instance so the spec will not fail if no instance receives the message. Scenario: allowing a message on any instance of a class Given a file named "example_spec.rb" with: diff --git a/features/method_stubs/README.md b/features/method_stubs/README.md index 9bfea21ae..81e6e883a 100644 --- a/features/method_stubs/README.md +++ b/features/method_stubs/README.md @@ -3,11 +3,11 @@ # create a double obj = double() - # specify a return value using `expect` syntax + # specify a return value using `:expect` syntax allow(obj).to receive(:message) { :value } allow(obj).to receive(:message).and_return(:value) - # specify a return value using `should` syntax + # specify a return value using `:should` syntax obj.stub(:message) { :value } obj.stub(:message => :value) obj.stub(:message).and_return(:value) @@ -20,12 +20,12 @@ block contents are evaluated lazily when the `obj` receives the allow(obj).to receive(:message) do |arg1, arg2| # set expectations about the args in this block - # and/or set a return value + # and/or return value end obj.stub(:message) do |arg1, arg2| # set expectations about the args in this block - # and/or set a return value + # and/or return a value end ### Raising/Throwing @@ -59,7 +59,7 @@ You can also use the block format: allow(obj).to receive(:message).with(an_instance_of(Money)) { ... } allow(obj).to receive(:message).with(hash_including(:a => 'b')) { ... } allow(obj).to receive(:message).with(array_including(1,2,3)) { ... } - or + # or allow(obj).to receive(:message).with(array_including([1,2,3])) { ... } obj.stub(:message).with(anything()) { ... } From 11dfcd5081f1e5e3e87262cebb3aa158cf2d14c1 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 27 Jun 2013 09:10:06 +1000 Subject: [PATCH 0065/1179] rewording double negatives --- features/message_expectations/allow_any_instance_of.feature | 2 +- features/message_expectations/expect_any_instance_of.feature | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/message_expectations/allow_any_instance_of.feature b/features/message_expectations/allow_any_instance_of.feature index 9c1ab609d..8e904bf51 100644 --- a/features/message_expectations/allow_any_instance_of.feature +++ b/features/message_expectations/allow_any_instance_of.feature @@ -17,7 +17,7 @@ Feature: allow a message on any instance of a class expect(o.foo).to eq(:return_value) end - it "wont fail if no instances receive that message" do + it "passes even if no instances receive that message" do o = Object.new end end diff --git a/features/message_expectations/expect_any_instance_of.feature b/features/message_expectations/expect_any_instance_of.feature index c15fe901b..03f4b03c5 100644 --- a/features/message_expectations/expect_any_instance_of.feature +++ b/features/message_expectations/expect_any_instance_of.feature @@ -17,7 +17,7 @@ Feature: expect a message on any instance of a class expect(o.foo).to eq(:return_value) end - it "fails if no instance receives that message" do + it "fails unless an instance receives that message" do o = Object.new end end From df7e8488ed9f9c156400f91d3aaad085ea3849b2 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 27 Jun 2013 09:50:42 +1000 Subject: [PATCH 0066/1179] ahem, reword assertion too --- features/message_expectations/expect_any_instance_of.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/message_expectations/expect_any_instance_of.feature b/features/message_expectations/expect_any_instance_of.feature index 03f4b03c5..b0b061814 100644 --- a/features/message_expectations/expect_any_instance_of.feature +++ b/features/message_expectations/expect_any_instance_of.feature @@ -24,4 +24,4 @@ Feature: expect a message on any instance of a class """ When I run `rspec example_spec.rb` Then the output should contain "2 examples, 1 failure" - And the output should contain "1) expect_any_instance_of fails if no instance receives that message" + And the output should contain "1) expect_any_instance_of fails unless an instance receives that message" From 3c1780a5147b22eb216456a812a5b436049c72cd Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 27 Jun 2013 16:23:17 +1000 Subject: [PATCH 0067/1179] update wording on allow_any_instance --- features/message_expectations/allow_any_instance_of.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/message_expectations/allow_any_instance_of.feature b/features/message_expectations/allow_any_instance_of.feature index 8e904bf51..5bf42dda6 100644 --- a/features/message_expectations/allow_any_instance_of.feature +++ b/features/message_expectations/allow_any_instance_of.feature @@ -1,8 +1,8 @@ Feature: allow a message on any instance of a class - Use `allow_any_instance_of(Class).to receive` when an instance of a class is - allowed to respond to a particular message. This will not set an expectation - on any instance so the spec will not fail if no instance receives the message. + Use `allow_any_instance_of(Class).to receive` when you want to configure how + instances of the given class respond to a message without setting an + expectation that the message will be received. Scenario: allowing a message on any instance of a class Given a file named "example_spec.rb" with: From 20c8dc4549dab39f88f1e95bee066aa963f0964e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 28 Jun 2013 13:01:10 +1000 Subject: [PATCH 0068/1179] Run tests using the client mode JVM (or as close as we can get to it) --- script/test_all | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/script/test_all b/script/test_all index 700625210..729ef8cd7 100755 --- a/script/test_all +++ b/script/test_all @@ -5,12 +5,17 @@ set -e -x # Needed by Bundler 1.3: https://github.com/carlhuda/bundler/issues/2382 export RUBYOPT='-rrbconfig' -echo "Bundling Standalone so we can run the specs w/o bundler loaded" +# idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html +export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived + +# force jRuby to use client mode JVM or a compilation mode thats as close as possible, +# idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time +export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' +echo "Bundling Standalone so we can run the specs w/o bundler loaded" bundle install --standalone --binstubs echo "Running all..." - bin/rspec spec --format progress --profile echo From fcbbaba69f18c1c38b73e0c3bf0bee5e974c4bd8 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 30 Jun 2013 15:05:47 +0200 Subject: [PATCH 0069/1179] Use syntax agnostic matchers for should syntax. Signed-off-by: Sam Phippen --- lib/rspec/mocks.rb | 10 ++++++---- lib/rspec/mocks/proxy_for_nil.rb | 2 +- lib/rspec/mocks/syntax.rb | 8 +++----- .../mocks/syntax_agnostic_message_matchers_spec.rb | 7 +++++++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index d89d9eb9d..5ff33281e 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -30,14 +30,16 @@ def any_instance_recorder_for(klass) space.any_instance_recorder_for(klass) end - def allow_message(subject, message, &block) + # Adds an allowance (stub) on `subject` + def allow_message(subject, message, opts={}, &block) ::RSpec::Mocks.proxy_for(subject). - add_stub(caller(1)[0], message.to_sym, &block) + add_stub(caller(1)[0], message.to_sym, opts, &block) end - def expect_message(subject, message, &block) + # Sets a message expectation on `subject`. + def expect_message(subject, message, opts={}, orig_caller=caller(1)[0], &block) ::RSpec::Mocks.proxy_for(subject). - add_message_expectation(caller(1)[0], message.to_sym, &block) + add_message_expectation(opts[:expected_from] || orig_caller, message.to_sym, opts, &block) end # @api private diff --git a/lib/rspec/mocks/proxy_for_nil.rb b/lib/rspec/mocks/proxy_for_nil.rb index 6ebeefc15..cd2d955ba 100644 --- a/lib/rspec/mocks/proxy_for_nil.rb +++ b/lib/rspec/mocks/proxy_for_nil.rb @@ -28,7 +28,7 @@ def add_stub(location, method_name, opts={}, &implementation) private def warn method_name - Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{caller[2]}. Use allow_message_expectations_on_nil to disable warnings.") + Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{caller[3]}. Use allow_message_expectations_on_nil to disable warnings.") end end diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 5bffa431c..74d4cb5fc 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -12,20 +12,18 @@ def self.enable_should(syntax_host = default_should_syntax_host) syntax_host.class_eval do def should_receive(message, opts={}, &block) - proxy = ::RSpec::Mocks.proxy_for(self) - proxy.add_message_expectation(opts[:expected_from] || caller(1)[0], message.to_sym, opts, &block) + ::RSpec::Mocks.expect_message(self, message.to_sym, opts, caller(1)[0], &block) end def should_not_receive(message, &block) - proxy = ::RSpec::Mocks.proxy_for(self) - proxy.add_negative_message_expectation(caller(1)[0], message.to_sym, &block) + ::RSpec::Mocks.expect_message(self, message.to_sym, {}, caller(1)[0], &block).never end def stub(message_or_hash, opts={}, &block) if ::Hash === message_or_hash message_or_hash.each {|message, value| stub(message).and_return value } else - ::RSpec::Mocks.proxy_for(self).add_stub(caller(1)[0], message_or_hash.to_sym, opts, &block) + ::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block) end end diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index 8a99947ac..16fab7213 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -46,6 +46,13 @@ module Mocks expect { verify subject }.to raise_error(RSpec::Mocks::MockExpectationError) end + it "fails if never is specified and the message is called" do + expect { + ::RSpec::Mocks.expect_message(subject, :foo).never + subject.foo + }.to raise_error(/expected.*0 times/) + end + it "sets up basic message expectation, verifies as called" do ::RSpec::Mocks.expect_message(subject, :basic) subject.basic From c1bdb71657aed38e782ed040399c0ee0a98c6f6e Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 30 Jun 2013 15:16:34 +0200 Subject: [PATCH 0070/1179] Add a negative expectation matcher to the syntax agnostic matchers. Signed-off-by: Sam Phippen --- lib/rspec/mocks.rb | 6 ++++++ lib/rspec/mocks/syntax.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 5ff33281e..e09922cba 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -42,6 +42,12 @@ def expect_message(subject, message, opts={}, orig_caller=caller(1)[0], &block) add_message_expectation(opts[:expected_from] || orig_caller, message.to_sym, opts, &block) end + # Sets a negative message expectation on `subject`. + def expect_no_message(subject, message, orig_caller=caller(1)[0], &block) + ::RSpec::Mocks.proxy_for(subject). + add_negative_message_expectation(orig_caller, message.to_sym, &block) + end + # @api private KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 74d4cb5fc..14def01a7 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -16,7 +16,7 @@ def should_receive(message, opts={}, &block) end def should_not_receive(message, &block) - ::RSpec::Mocks.expect_message(self, message.to_sym, {}, caller(1)[0], &block).never + ::RSpec::Mocks.expect_no_message(self, message.to_sym, caller(1)[0], &block) end def stub(message_or_hash, opts={}, &block) From 451a78b15f55f99a20eb93b1d89e74cad19d4b0c Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 30 Jun 2013 23:38:20 +0200 Subject: [PATCH 0071/1179] Pass callers consistently in syntax-agnostic message matchers. Signed-off-by: Sam Phippen --- lib/rspec/mocks.rb | 11 +++++++---- lib/rspec/mocks/syntax.rb | 7 +++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index e09922cba..a388c652a 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -32,18 +32,21 @@ def any_instance_recorder_for(klass) # Adds an allowance (stub) on `subject` def allow_message(subject, message, opts={}, &block) + orig_caller = opts.fetch(:expected_from) { caller(1)[0] } ::RSpec::Mocks.proxy_for(subject). - add_stub(caller(1)[0], message.to_sym, opts, &block) + add_stub(orig_caller, message.to_sym, opts, &block) end # Sets a message expectation on `subject`. - def expect_message(subject, message, opts={}, orig_caller=caller(1)[0], &block) + def expect_message(subject, message, opts={}, &block) + orig_caller = opts.fetch(:expected_from) { caller(1)[0] } ::RSpec::Mocks.proxy_for(subject). - add_message_expectation(opts[:expected_from] || orig_caller, message.to_sym, opts, &block) + add_message_expectation(orig_caller, message.to_sym, opts, &block) end # Sets a negative message expectation on `subject`. - def expect_no_message(subject, message, orig_caller=caller(1)[0], &block) + def expect_no_message(subject, message, opts, &block) + orig_caller = opts.fetch(:expected_from) { caller(1)[0] } ::RSpec::Mocks.proxy_for(subject). add_negative_message_expectation(orig_caller, message.to_sym, &block) end diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 14def01a7..4fb5d672a 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -12,17 +12,20 @@ def self.enable_should(syntax_host = default_should_syntax_host) syntax_host.class_eval do def should_receive(message, opts={}, &block) - ::RSpec::Mocks.expect_message(self, message.to_sym, opts, caller(1)[0], &block) + opts[:expected_from] ||= caller(1)[0] + ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block) end def should_not_receive(message, &block) - ::RSpec::Mocks.expect_no_message(self, message.to_sym, caller(1)[0], &block) + opts = {:expected_from => caller(1)[0]} + ::RSpec::Mocks.expect_no_message(self, message.to_sym, opts, &block) end def stub(message_or_hash, opts={}, &block) if ::Hash === message_or_hash message_or_hash.each {|message, value| stub(message).and_return value } else + opts[:expected_from] = caller(1)[0] ::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block) end end From 5316bff7121681d3f92cbceb5fa18ac809f43ca6 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 30 Jun 2013 23:54:20 +0200 Subject: [PATCH 0072/1179] Use the first caller that isn't in rspec-mocks for expectations on nil. Signed-off-by: Sam Phippen --- lib/rspec/mocks/proxy_for_nil.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/proxy_for_nil.rb b/lib/rspec/mocks/proxy_for_nil.rb index cd2d955ba..434e08390 100644 --- a/lib/rspec/mocks/proxy_for_nil.rb +++ b/lib/rspec/mocks/proxy_for_nil.rb @@ -28,7 +28,8 @@ def add_stub(location, method_name, opts={}, &implementation) private def warn method_name - Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{caller[3]}. Use allow_message_expectations_on_nil to disable warnings.") + non_rspec_caller = caller.find { |line| !line.include?('lib/rspec/mocks') } + Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{non_rspec_caller}. Use allow_message_expectations_on_nil to disable warnings.") end end From acc0dc426538b2dc9146e3abd5fb9471b329e45a Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 30 Jun 2013 23:59:07 +0200 Subject: [PATCH 0073/1179] Fix up the specs around message allowances with block implementations. Signed-off-by: Sam Phippen --- spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index 16fab7213..c53c8b242 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -26,11 +26,15 @@ module Mocks expect(subject.x(:in)).to eq(:out) end - it "accepts a callable responder for the message" do + it "supports block implementations" do ::RSpec::Mocks.allow_message(subject, :message) { :value } - expect(subject.message).to eq(:value) + expect(subject.respond_to? :message).to be_true end + it "does not set an expectation that the message will be received" do + ::RSpec::Mocks.allow_message(subject, :message) + expect { verify subject }.not_to raise_error + end end describe ".expect_message" do From 77b64fe37c440caa008dbc29c25e4e356711f4a5 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 30 Jun 2013 22:53:06 -0400 Subject: [PATCH 0074/1179] Resolves rspec/rspec-core#950 and adds specs * Hopefully the code clarity is improved here * We also change the behavior by *not* duping an object if `space` is not yet initialized. This should have been the behavior from the beginning, but it was overlooked and did not have specs. --- lib/rspec/mocks/extensions/marshal.rb | 14 +++--- spec/rspec/mocks/extensions/marshal_spec.rb | 48 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 spec/rspec/mocks/extensions/marshal_spec.rb diff --git a/lib/rspec/mocks/extensions/marshal.rb b/lib/rspec/mocks/extensions/marshal.rb index 7698c1887..e0cc4d60d 100644 --- a/lib/rspec/mocks/extensions/marshal.rb +++ b/lib/rspec/mocks/extensions/marshal.rb @@ -1,13 +1,13 @@ module Marshal class << self - def dump_with_mocks(*args) - object = args.shift - - if ( ::RSpec::Mocks.space && !::RSpec::Mocks.space.registered?(object) ) || NilClass === object - return dump_without_mocks(*args.unshift(object)) + # Duplicates any mock objects before serialization. Otherwise, + # serialization will fail because methods exist on the singleton class. + def dump_with_mocks(object, *rest) + if !::RSpec::Mocks.space || !::RSpec::Mocks.space.registered?(object) || NilClass === object + dump_without_mocks(object, *rest) + else + dump_without_mocks(object.dup, *rest) end - - dump_without_mocks(*args.unshift(object.dup)) end alias_method :dump_without_mocks, :dump diff --git a/spec/rspec/mocks/extensions/marshal_spec.rb b/spec/rspec/mocks/extensions/marshal_spec.rb new file mode 100644 index 000000000..cce039a3c --- /dev/null +++ b/spec/rspec/mocks/extensions/marshal_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe Marshal, 'extensions' do + # An object that raises when code attempts to dup it. + # + # Because we manipulate the internals of RSpec::Mocks.space below, we need + # an object that simply blows up when #dup is called without using any + # partial mocking or stubbing from rspec-mocks itself. + class UndupableObject + def dup + raise NotImplementedError + end + end + + describe '#dump' do + context 'when rspec-mocks has not been fully initialized' do + def without_space + stashed_space, RSpec::Mocks.space = RSpec::Mocks.space, nil + yield + ensure + RSpec::Mocks.space = stashed_space + end + + it 'does not duplicate the object before serialization' do + obj = UndupableObject.new + without_space do + expect { Marshal.dump(obj) }.not_to raise_error + end + end + end + + context 'when rspec-mocks has been fully initialized' do + it 'duplicates objects with stubbed or mocked implementations before serialization' do + obj = double(:foo => "bar") + expect { Marshal.dump(obj) }.not_to raise_error + end + + it 'does not duplicate other objects before serialization' do + obj = UndupableObject.new + expect { Marshal.dump(obj) }.not_to raise_error + end + + it 'does not duplicate nil before serialization' do + expect { Marshal.dump(nil) }.not_to raise_error + end + end + end +end From dbe22d20779c59228b1c825f525e9d58b949ec57 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 30 Jun 2013 21:33:36 -0700 Subject: [PATCH 0075/1179] Fix spec so that it actually tests what it is meant to. The original expectation would pass even if this method did not support a block implementation. --- spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index c53c8b242..33b401669 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -28,7 +28,7 @@ module Mocks it "supports block implementations" do ::RSpec::Mocks.allow_message(subject, :message) { :value } - expect(subject.respond_to? :message).to be_true + expect(subject.message).to eq(:value) end it "does not set an expectation that the message will be received" do From 9357e4de76838157c8ea7d4a4ad6d0ae11882b22 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 30 Jun 2013 21:46:19 -0700 Subject: [PATCH 0076/1179] Reconcile differences between two different ways to set a negative expectation. `expect().not_to receive`/`should_not_receive` (form A) vs `expect().to receive().never`/`should_receive().never` (form B) had slightly different behavior even though they are intended to work the same. Specifically: - Form A would cause an error to be raised when `and_return` was chained off of it, since that is nonsensical. Form B did not raise this error. - Form A supported a negation for particular arguments specified with `with`, but Form B did not - it was always a universal negation for all possible arguments. I've reconciling this difference by making Form A simply delegate to Form B under the covers. This allowed use to get rid of a bunch of code. --- lib/rspec/mocks.rb | 7 --- .../mocks/any_instance/expectation_chain.rb | 29 +----------- lib/rspec/mocks/any_instance/recorder.rb | 3 +- lib/rspec/mocks/matchers/receive.rb | 6 ++- lib/rspec/mocks/message_expectation.rb | 46 ++++++++----------- lib/rspec/mocks/method_double.rb | 9 ---- lib/rspec/mocks/proxy.rb | 5 -- lib/rspec/mocks/syntax.rb | 2 +- spec/rspec/mocks/mock_spec.rb | 18 +++++++- spec/rspec/mocks/partial_mock_spec.rb | 6 +-- 10 files changed, 46 insertions(+), 85 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index a388c652a..9e596c0a4 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -44,13 +44,6 @@ def expect_message(subject, message, opts={}, &block) add_message_expectation(orig_caller, message.to_sym, opts, &block) end - # Sets a negative message expectation on `subject`. - def expect_no_message(subject, message, opts, &block) - orig_caller = opts.fetch(:expected_from) { caller(1)[0] } - ::RSpec::Mocks.proxy_for(subject). - add_negative_message_expectation(orig_caller, message.to_sym, &block) - end - # @api private KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index c826377ea..699a2153b 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -36,34 +36,7 @@ def invocation_order } end end - - # @api private - class NegativeExpectationChain < ExpectationChain - # `should_not_receive` causes a failure at the point in time the - # message is wrongly received, rather than during `rspec_verify` - # at the end of an example. Thus, we should always consider a - # negative expectation fulfilled for the purposes of end-of-example - # verification (which is where this is used). - def expectation_fulfilled? - true - end - - private - - def create_message_expectation_on(instance) - proxy = ::RSpec::Mocks.proxy_for(instance) - expected_from = IGNORED_BACKTRACE_LINE - proxy.add_negative_message_expectation(expected_from, *@expectation_args, &@expectation_block) - end - - def invocation_order - @invocation_order ||= { - :with => [nil], - :and_return => [:with, nil], - :and_raise => [:with, nil] - } - end - end end end end + diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 27a4cacfc..64b0a8fad 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -60,8 +60,7 @@ def should_receive(method_name, &block) end def should_not_receive(method_name, &block) - observe!(method_name) - message_chains.add(method_name, NegativeExpectationChain.new(method_name, &block)) + should_receive(method_name, &block).never end # Removes any previously recorded stubs, stub_chains or message diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 89082da29..824d81575 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -22,7 +22,11 @@ def setup_expectation(subject, &block) alias matches? setup_expectation def setup_negative_expectation(subject, &block) - setup_mock_proxy_method_substitute(subject, :add_negative_message_expectation, block) + # ensure `never` goes first for cases like `never.and_return(5)`, + # where `and_return` is meant to raise an error + @recorded_customizations.unshift Customization.new(:never, [], nil) + + setup_expectation(subject, &block) end alias does_not_match? setup_negative_expectation diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index f28ce6dcb..a65f7b9b4 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -73,16 +73,20 @@ def expected_args # counter.stub(:count) { 1 } # counter.count # => 1 def and_return(*values, &implementation) - @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least) - - if implementation - # TODO: deprecate `and_return { value }` - self.inner_implementation_action = implementation + if negative? + raise "`and_return` is not supported with negative message expectations" else - self.terminal_implementation_action = AndReturnImplementation.new(values) - end + @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least) - nil + if implementation + # TODO: deprecate `and_return { value }` + self.inner_implementation_action = implementation + else + self.terminal_implementation_action = AndReturnImplementation.new(values) + end + + nil + end end # Tells the object to delegate to the original unmodified method @@ -169,7 +173,7 @@ def matches?(message, *args) # @private def invoke(parent_stub, *args, &block) - if (@expected_received_count == 0 && !@at_least) || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count)) + if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count)) @actual_received_count += 1 @failed_fast = true #args are the args we actually received, @argument_list_matcher is the @@ -190,6 +194,11 @@ def invoke(parent_stub, *args, &block) end end + # @private + def negative? + @expected_received_count == 0 && !@at_least + end + # @private def called_max_times? @expected_received_count != :any && @@ -409,7 +418,7 @@ def ordered(&block) # @private def negative_expectation_for?(message) - return false + @message == message && negative? end # @private @@ -452,23 +461,6 @@ def terminal_implementation_action=(action) end end - # @private - class NegativeMessageExpectation < MessageExpectation - # @private - def initialize(error_generator, expectation_ordering, expected_from, method_double, &implementation) - super(error_generator, expectation_ordering, expected_from, method_double, 0, {}, &implementation) - end - - def and_return(*) - raise "and_return is not supported with negative message expectations" - end - - # @private - def negative_expectation_for?(message) - return @message == message - end - end - # Handles the implementation of an `and_yield` declaration. # @private class AndYieldImplementation diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 64b6a0336..268e77ba9 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -226,15 +226,6 @@ def add_expectation(error_generator, expectation_ordering, expected_from, opts, expectation end - # @private - def add_negative_expectation(error_generator, expectation_ordering, expected_from, &implementation) - configure_method - expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering, - expected_from, self, &implementation) - expectations.unshift expectation - expectation - end - # @private def build_expectation(error_generator, expectation_ordering) expected_from = IGNORED_BACKTRACE_LINE diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 74760de76..5dc63b40f 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -51,11 +51,6 @@ def add_message_expectation(location, method_name, opts={}, &block) meth_double.add_expectation @error_generator, @expectation_ordering, location, opts, &block end - # @private - def add_negative_message_expectation(location, method_name, &implementation) - method_double[method_name].add_negative_expectation @error_generator, @expectation_ordering, location, &implementation - end - # @private def build_expectation(method_name) meth_double = method_double[method_name] diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 4fb5d672a..2fc55b146 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -18,7 +18,7 @@ def should_receive(message, opts={}, &block) def should_not_receive(message, &block) opts = {:expected_from => caller(1)[0]} - ::RSpec::Mocks.expect_no_message(self, message.to_sym, opts, &block) + ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block).never end def stub(message_or_hash, opts={}, &block) diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 6a6a1f85c..1c62d96f9 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -42,8 +42,18 @@ module Mocks verify @double end - it "warns when should_not_receive is followed by and_return" do - expect { @double.should_not_receive(:do_something).and_return(1) }.to raise_error(/not supported/) + it "warns when `and_return` is called on a negative expectation" do + expect { + @double.should_not_receive(:do_something).and_return(1) + }.to raise_error(/not supported/) + + expect { + expect(@double).not_to receive(:do_something).and_return(1) + }.to raise_error(/not supported/) + + expect { + expect(@double).to receive(:do_something).never.and_return(1) + }.to raise_error(/not supported/) end it "passes when receiving message specified as not to be received with different args" do @@ -90,6 +100,10 @@ module Mocks @double.should_not_receive(:not_expected).with("unexpected text") @double.not_expected "really unexpected text" verify @double + + @double.should_receive(:not_expected).with("unexpected text").never + @double.not_expected "really unexpected text" + verify @double end it "allows block to calculate return values" do diff --git a/spec/rspec/mocks/partial_mock_spec.rb b/spec/rspec/mocks/partial_mock_spec.rb index 013815823..e5bd34ade 100644 --- a/spec/rspec/mocks/partial_mock_spec.rb +++ b/spec/rspec/mocks/partial_mock_spec.rb @@ -36,7 +36,7 @@ module Mocks end it "should_not_receive returns a negative message expectation" do - expect(object.should_not_receive(:foobar)).to be_kind_of(RSpec::Mocks::NegativeMessageExpectation) + expect(object.should_not_receive(:foobar)).to be_negative end it "should_receive mocks out the method" do @@ -55,8 +55,8 @@ module Mocks expect(object.foobar(hash[:a])).to equal(1) end - it "should_receive returns a message expectation" do - expect(object.should_receive(:foobar)).to be_kind_of(RSpec::Mocks::MessageExpectation) + it "should_receive returns a positive message expectation" do + expect(object.should_receive(:foobar)).not_to be_negative object.foobar end From aee79a7292288875631ab91fd18c5064cb685325 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 30 Jun 2013 23:19:08 -0700 Subject: [PATCH 0077/1179] Prevent confusing double negative expressions. Examples: expect(obj).not_to receive(:msg).never obj.should_not_receive(:msg).never expect_any_instance_of(kls).not_to receive(:msg).never kls.any_instance.should_not_receive(:msg).never These expressions make no sense and should not be supported. --- lib/rspec/mocks/any_instance/chain.rb | 45 ++++++++++++++--------- lib/rspec/mocks/error_generator.rb | 7 ++++ lib/rspec/mocks/message_expectation.rb | 1 + spec/rspec/mocks/any_instance_spec.rb | 6 +++ spec/rspec/mocks/matchers/receive_spec.rb | 10 +++++ spec/rspec/mocks/mock_spec.rb | 6 +++ 6 files changed, 57 insertions(+), 18 deletions(-) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 81f4ad164..04b985a19 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -7,9 +7,7 @@ def initialize(*args, &block) @expectation_block = block end - class << self - private - + module Customizations # @macro [attach] record # @method $1(*args, &block) # Records the `$1` message for playback against an instance that @@ -17,29 +15,31 @@ class << self # # @see RSpec::Mocks::MessageExpectation#$1 # - def record(method_name) + def self.record(method_name) class_eval(<<-EOM, __FILE__, __LINE__ + 1) def #{method_name}(*args, &block) record(:#{method_name}, *args, &block) end EOM end + + record :and_return + record :and_raise + record :and_throw + record :and_yield + record :and_call_original + record :with + record :once + record :twice + record :any_number_of_times + record :exactly + record :times + record :never + record :at_least + record :at_most end - record :and_return - record :and_raise - record :and_throw - record :and_yield - record :and_call_original - record :with - record :once - record :twice - record :any_number_of_times - record :exactly - record :times - record :never - record :at_least - record :at_most + include Customizations # @private def playback!(instance) @@ -63,8 +63,17 @@ def expectation_fulfilled! @expectation_fulfilled = true end + def never + ErrorGenerator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated? + super + end + private + def negated? + messages.any? { |(message, *_), _| message.to_sym == :never } + end + def messages @messages ||= [] end diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 36ef53f51..6e39238a7 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -116,6 +116,13 @@ def raise_expectation_on_mocked_method(method) "method has been mocked instead of stubbed." end + def self.raise_double_negation_error(wrapped_expression) + raise "Isn't life confusing enough? You've already set a " + + "negative message expectation and now you are trying to " + + "negate it again with `never`. What does an expression like " + + "`#{wrapped_expression}.not_to receive(:msg).never` even mean?" + end + private def intro diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index a65f7b9b4..3e5c2f3ef 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -376,6 +376,7 @@ def any_number_of_times(&block) # # car.should_receive(:stop).never def never + ErrorGenerator.raise_double_negation_error("expect(obj)") if negative? @expected_received_count = 0 self end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index f9522d403..d4768669d 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -347,6 +347,12 @@ class RSpec::SampleRspecTestClass;end RSpec::Mocks.space.verify_all end end + + it "prevents confusing double-negative expressions involving `never`" do + expect { + klass.any_instance.should_not_receive(:not_expected).never + }.to raise_error(/trying to negate it again/) + end end context "with #should_receive" do diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 33ec64b19..9e4a50466 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -136,6 +136,16 @@ def receiver.method_missing(*a); end # a poor man's stub... receiver.foo(:a) }.to raise_error(/expected: 0 times.*received: 1 time/m) end + + it 'prevents confusing double-negative expressions involving `never`' do + expect { + wrapped.not_to receive(:foo).never + }.to raise_error(/trying to negate it again/) + + expect { + wrapped.to_not receive(:foo).never + }.to raise_error(/trying to negate it again/) + end end describe "allow(...).to receive" do diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 1c62d96f9..feddf6ba3 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -42,6 +42,12 @@ module Mocks verify @double end + it "prevents confusing double-negative expressions involving `never`" do + expect { + @double.should_not_receive(:not_expected).never + }.to raise_error(/trying to negate it again/) + end + it "warns when `and_return` is called on a negative expectation" do expect { @double.should_not_receive(:do_something).and_return(1) From cfd4f0cb13f7da43b9754f38c26a86cb5dc730c1 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Mon, 1 Jul 2013 11:42:59 +0200 Subject: [PATCH 0078/1179] Flesh out the yard docs for the syntax agnostic message matchers. Signed-off-by: Sam Phippen --- lib/rspec/mocks.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 9e596c0a4..3ff5d796f 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -31,6 +31,17 @@ def any_instance_recorder_for(klass) end # Adds an allowance (stub) on `subject` + # + # @param subject the subject to which the message will be added + # @param message a symbol, representing the message that will be + # added. + # @param opts a hash of options, :expected_from is used to set the + # original call site + # @param block an optional implementation fo rhte allowance + # + # @example Add the message `foo` to object `bar`, using the passed block + # x = 0 + # allow_message(bar, :foo) { x += 1 } def allow_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { caller(1)[0] } ::RSpec::Mocks.proxy_for(subject). @@ -38,6 +49,12 @@ def allow_message(subject, message, opts={}, &block) end # Sets a message expectation on `subject`. + # @param subject the subject on which the message will be expected + # @param message a symbol, representing the message that will be + # expected. + # @param opts a hash of options, :expected_from is used to set the + # original call site + # @param block an optional implementation fo rhte allowance def expect_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { caller(1)[0] } ::RSpec::Mocks.proxy_for(subject). From 75ad1ed872712efacea66ce8b52eae55b931107c Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Mon, 1 Jul 2013 11:49:29 +0200 Subject: [PATCH 0079/1179] Add a changelog entry for #311 and #334. Signed-off-by: Sam Phippen --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 74b73e86c..012fcb138 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,9 @@ Enhancements: * Document test spies in the readme. (Adarsh Pandit) * Add an `array_including` matcher. (Sam Phippen) +* Create syntax agnostic message matchers enable message allowances and + expectations to be set independently of any particular syntax being + enabled (Paul Anneesley, Myron Marston and Sam Phippen). Bug Fixes: From fe33bf6708a5c22a9129e427ec8c5f5483f730ef Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Mon, 1 Jul 2013 12:04:22 +0200 Subject: [PATCH 0080/1179] Fix the yard docs for stuff I typod/didn't commit. Signed-off-by: Sam Phippen --- lib/rspec/mocks.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 3ff5d796f..320c69e6c 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -54,7 +54,11 @@ def allow_message(subject, message, opts={}, &block) # expected. # @param opts a hash of options, :expected_from is used to set the # original call site - # @param block an optional implementation fo rhte allowance + # @param block an optional implementation for the allowance + # + # @example Expect the message `foo` to object `bar`, then call it + # expect_message(bar, :foo) + # bar.foo def expect_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { caller(1)[0] } ::RSpec::Mocks.proxy_for(subject). From 82ada28657f206ef317c46c94a5aa39409135a5f Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Mon, 1 Jul 2013 21:09:55 -0400 Subject: [PATCH 0081/1179] Improves clarity of nil check --- lib/rspec/mocks/extensions/marshal.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/extensions/marshal.rb b/lib/rspec/mocks/extensions/marshal.rb index e0cc4d60d..b8cf7b5df 100644 --- a/lib/rspec/mocks/extensions/marshal.rb +++ b/lib/rspec/mocks/extensions/marshal.rb @@ -3,7 +3,7 @@ class << self # Duplicates any mock objects before serialization. Otherwise, # serialization will fail because methods exist on the singleton class. def dump_with_mocks(object, *rest) - if !::RSpec::Mocks.space || !::RSpec::Mocks.space.registered?(object) || NilClass === object + if ::RSpec::Mocks.space.nil? || !::RSpec::Mocks.space.registered?(object) || NilClass === object dump_without_mocks(object, *rest) else dump_without_mocks(object.dup, *rest) From ed527b92b9437dab68cdc7bcb3c98ddefb44dd0d Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Mon, 1 Jul 2013 21:16:11 -0400 Subject: [PATCH 0082/1179] Improves strength of assertions per @myronmarston's suggestions --- spec/rspec/mocks/extensions/marshal_spec.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/rspec/mocks/extensions/marshal_spec.rb b/spec/rspec/mocks/extensions/marshal_spec.rb index cce039a3c..46f436b34 100644 --- a/spec/rspec/mocks/extensions/marshal_spec.rb +++ b/spec/rspec/mocks/extensions/marshal_spec.rb @@ -24,7 +24,8 @@ def without_space it 'does not duplicate the object before serialization' do obj = UndupableObject.new without_space do - expect { Marshal.dump(obj) }.not_to raise_error + serialized = Marshal.dump(obj) + expect(Marshal.load(serialized)).to be_an(UndupableObject) end end end @@ -32,16 +33,21 @@ def without_space context 'when rspec-mocks has been fully initialized' do it 'duplicates objects with stubbed or mocked implementations before serialization' do obj = double(:foo => "bar") - expect { Marshal.dump(obj) }.not_to raise_error + + serialized = Marshal.dump(obj) + expect(Marshal.load(serialized)).to be_an(obj.class) end it 'does not duplicate other objects before serialization' do obj = UndupableObject.new - expect { Marshal.dump(obj) }.not_to raise_error + + serialized = Marshal.dump(obj) + expect(Marshal.load(serialized)).to be_an(UndupableObject) end it 'does not duplicate nil before serialization' do - expect { Marshal.dump(nil) }.not_to raise_error + serialized = Marshal.dump(nil) + expect(Marshal.load(serialized)).to be_nil end end end From bcee05639bc4668959485dc8627132d29e96e3e8 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Mon, 1 Jul 2013 23:01:39 -0400 Subject: [PATCH 0083/1179] Changelog entry --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 74b73e86c..a24d4dc83 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,9 @@ Bug Fixes: `and_yield`, `and_raise`, `and_return` or `and_throw`. This got fixed in 2.13.1 but failed to get merged into master for the 2.14.0.rc1 release (Myron Marston). +* `Marshal.dump` does not unnecessarily duplicate objects when rspec-mocks has + not been fully initialized. This could cause errors when using `spork` or + similar preloading gems (Andy Lindeman). ### 2.14.0.rc1 / 2013-05-27 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) From 6b5e35348e06be1fa976afda15d66d0675e87611 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 3 Jul 2013 22:21:49 +1000 Subject: [PATCH 0084/1179] make @myronmarstons typo corrections --- lib/rspec/mocks.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 320c69e6c..8337942e9 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -37,9 +37,9 @@ def any_instance_recorder_for(klass) # added. # @param opts a hash of options, :expected_from is used to set the # original call site - # @param block an optional implementation fo rhte allowance + # @param block an optional implementation for the allowance # - # @example Add the message `foo` to object `bar`, using the passed block + # @example Defines the implementation of `foo` on `bar`, using the passed block # x = 0 # allow_message(bar, :foo) { x += 1 } def allow_message(subject, message, opts={}, &block) @@ -54,9 +54,9 @@ def allow_message(subject, message, opts={}, &block) # expected. # @param opts a hash of options, :expected_from is used to set the # original call site - # @param block an optional implementation for the allowance + # @param block an optional implementation for the expectation # - # @example Expect the message `foo` to object `bar`, then call it + # @example Expect the message `foo` to receive `bar`, then call it # expect_message(bar, :foo) # bar.foo def expect_message(subject, message, opts={}, &block) From 90997a5faec68228bd30e8e7f98b6cbdacb3612a Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 3 Jul 2013 22:23:02 +0200 Subject: [PATCH 0085/1179] Show the necessary RSpec::Mocks prefix in yard docs. Signed-off-by: Sam Phippen --- lib/rspec/mocks.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 8337942e9..ba724eac7 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -41,7 +41,7 @@ def any_instance_recorder_for(klass) # # @example Defines the implementation of `foo` on `bar`, using the passed block # x = 0 - # allow_message(bar, :foo) { x += 1 } + # RSpec::Mocks.allow_message(bar, :foo) { x += 1 } def allow_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { caller(1)[0] } ::RSpec::Mocks.proxy_for(subject). @@ -57,7 +57,7 @@ def allow_message(subject, message, opts={}, &block) # @param block an optional implementation for the expectation # # @example Expect the message `foo` to receive `bar`, then call it - # expect_message(bar, :foo) + # RSpec::Mocks.expect_message(bar, :foo) # bar.foo def expect_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { caller(1)[0] } From 39ab4ad193928409f83d624e34de9a372d797878 Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Thu, 4 Jul 2013 15:41:26 +1000 Subject: [PATCH 0086/1179] Typo in name in Changelog; Paul Annesley. --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index e8556bde0..615de8891 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,7 +12,7 @@ Enhancements: * Add an `array_including` matcher. (Sam Phippen) * Create syntax agnostic message matchers enable message allowances and expectations to be set independently of any particular syntax being - enabled (Paul Anneesley, Myron Marston and Sam Phippen). + enabled (Paul Annesley, Myron Marston and Sam Phippen). Bug Fixes: From d4daf1474d765b6450af571137bc1824de283d0a Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 Jul 2013 19:05:20 +0200 Subject: [PATCH 0087/1179] Remove `stub!` and `unstub!`. Signed-off-by: Sam Phippen --- Changelog.md | 1 + lib/rspec/mocks/any_instance/recorder.rb | 10 --------- lib/rspec/mocks/syntax.rb | 12 ----------- spec/rspec/mocks/any_instance_spec.rb | 16 --------------- spec/rspec/mocks/methods_spec.rb | 2 +- spec/rspec/mocks/stub_spec.rb | 26 ------------------------ 6 files changed, 2 insertions(+), 65 deletions(-) diff --git a/Changelog.md b/Changelog.md index 615de8891..7a4b7548c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Breaking Changes for 3.0.0: * Raise an explicit error if `should_not_receive(...).and_return` is used. (Sam Phippen) * Remove 1.8.6 workarounds (Jon Rowe) +* Remove `stub!` and `unstub!`. (Sam Phippen) Enhancements: diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 64b0a8fad..fcdd9874b 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -88,16 +88,6 @@ def verify ::RSpec::Mocks.space.remove_any_instance_recorder_for(@klass) end - # @private - def stub!(*) - raise "stub! is not supported on any_instance. Use stub instead." - end - - # @private - def unstub!(*) - raise "unstub! is not supported on any_instance. Use unstub instead." - end - # @private def stop_all_observation! @observed_methods.each {|method_name| restore_method!(method_name)} diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 2fc55b146..a1cefe1a8 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -34,16 +34,6 @@ def unstub(message) ::RSpec::Mocks.space.proxy_for(self).remove_stub(message) end - def stub!(message_or_hash, opts={}, &block) - ::RSpec.deprecate "stub!", :replacement => "stub" - stub(message_or_hash, opts={}, &block) - end - - def unstub!(message) - ::RSpec.deprecate "unstub!", :replacement => "unstub" - unstub(message) - end - def stub_chain(*chain, &blk) ::RSpec::Mocks::StubChain.stub_chain_on(self, *chain, &blk) end @@ -81,8 +71,6 @@ def self.disable_should(syntax_host = default_should_syntax_host) undef should_not_receive undef stub undef unstub - undef stub! - undef unstub! undef stub_chain undef as_null_object undef null_object? diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index d4768669d..e0dad4e10 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -265,22 +265,6 @@ class RSpec::SampleRspecTestClass;end end end - context "with #stub!" do - it "raises with a message instructing the user to use stub instead" do - expect do - klass.any_instance.stub!(:foo) - end.to raise_error(/Use stub instead/) - end - end - - context "with #unstub!" do - it "raises with a message instructing the user to use unstub instead" do - expect do - klass.any_instance.unstub!(:foo) - end.to raise_error(/Use unstub instead/) - end - end - context "unstub implementation" do it "replaces the stubbed method with the original method" do klass.any_instance.stub(:existing_method) diff --git a/spec/rspec/mocks/methods_spec.rb b/spec/rspec/mocks/methods_spec.rb index 0c9ce76d2..9c80b2ff4 100644 --- a/spec/rspec/mocks/methods_spec.rb +++ b/spec/rspec/mocks/methods_spec.rb @@ -18,7 +18,7 @@ def added_methods expect(added_methods).to match_array([ :as_null_object, :null_object?, :received_message?, :should_not_receive, :should_receive, - :stub, :stub!, :stub_chain, :unstub, :unstub! + :stub, :stub_chain, :unstub ]) end end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 0d8b5ebc3..1acad9481 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -37,32 +37,6 @@ def existing_private_instance_method end end - describe "using stub!" do - it "warns of deprecation but still returns the declared value when message is received" do - expect(RSpec).to receive(:deprecate).with("stub!", :replacement => "stub") - @instance.stub!(:msg).and_return(:return_value) - expect(@instance.msg).to equal(:return_value) - verify @instance - end - end - - describe 'using unstub' do - it 'removes the message stub' do - @instance.stub(:msg) - @instance.unstub(:msg) - expect { @instance.msg }.to raise_error NoMethodError - end - end - - describe 'using unstub!' do - it 'removes the message stub but warns about deprecation' do - @instance.stub(:msg) - RSpec.should_receive(:deprecate).with("unstub!", :replacement => "unstub") - @instance.unstub!(:msg) - expect { @instance.msg }.to raise_error NoMethodError - end - end - it "instructs an instance to respond_to the message" do @instance.stub(:msg) expect(@instance).to respond_to(:msg) From 29a1cab15ee35d416374aa54518863cc9642e807 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Thu, 4 Jul 2013 13:59:58 -0400 Subject: [PATCH 0088/1179] Replace string eval with define_method In the majority of instances, using define_method over eval'ing a String is better, and safer. See: http://tenderlovemaking.com/2013/03/03/dynamic_method_definitions.html --- lib/rspec/mocks/any_instance/chain.rb | 8 +++----- lib/rspec/mocks/any_instance/recorder.rb | 25 ++++++++++++----------- lib/rspec/mocks/matchers/receive.rb | 10 ++++----- lib/rspec/mocks/targets.rb | 8 +++----- spec/rspec/mocks/matchers/receive_spec.rb | 2 +- 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 04b985a19..6168199b2 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -16,11 +16,9 @@ module Customizations # @see RSpec::Mocks::MessageExpectation#$1 # def self.record(method_name) - class_eval(<<-EOM, __FILE__, __LINE__ + 1) - def #{method_name}(*args, &block) - record(:#{method_name}, *args, &block) - end - EOM + define_method(method_name) do |*args, &block| + record(method_name.to_sym, *args, &block) + end end record :and_return diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 64b0a8fad..466a2b361 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -180,25 +180,26 @@ def observe!(method_name) stop_observing!(method_name) if already_observing?(method_name) @observed_methods << method_name backup_method!(method_name) - @klass.class_eval(<<-EOM, __FILE__, __LINE__ + 1) - def #{method_name}(*args, &blk) - klass = ::RSpec::Mocks.method_handle_for(self, :#{method_name}).owner - ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, :#{method_name}) - self.__send__(:#{method_name}, *args, &blk) + @klass.class_eval do + define_method(method_name) do |*args, &blk| + method_name = method_name.to_sym + klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner + ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name) + self.__send__(method_name.to_sym, *args, &blk) end - EOM + end end def mark_invoked!(method_name) backup_method!(method_name) - @klass.class_eval(<<-EOM, __FILE__, __LINE__ + 1) - def #{method_name}(*args, &blk) - method_name = :#{method_name} - klass = ::RSpec::Mocks.method_handle_for(self, :#{method_name}).owner + @klass.class_eval do + define_method(method_name) do |*args, &blk| + method_name = method_name.to_sym + klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name) - raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by \#{self.inspect} but has already been received by \#{invoked_instance}" + raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{self.inspect} but has already been received by #{invoked_instance}" end - EOM + end end end end diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 824d81575..f4ea73ede 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -49,12 +49,10 @@ def setup_any_instance_allowance(subject, &block) MessageExpectation.public_instance_methods(false).each do |method| next if method_defined?(method) - class_eval(<<-RUBY) - def #{method}(*args, &block) - @recorded_customizations << Customization.new(#{method.inspect}, args, block) - self - end - RUBY + define_method(method) do |*args, &block| + @recorded_customizations << Customization.new(method, args, block) + self + end end private diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 4430d014a..88aa5928e 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -10,16 +10,14 @@ def initialize(target) def self.delegate_to(matcher_method, options = {}) method_name = options.fetch(:from) { :to } - class_eval(<<-RUBY) - def #{method_name}(matcher, &block) + define_method(method_name) do |matcher, &block| unless Matchers::Receive === matcher raise UnsupportedMatcherError, "only the `receive` matcher is supported " + - "with `\#{expression}(...).\#{#{method_name.inspect}}`, but you have provided: \#{matcher}" + "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" end - matcher.__send__(#{matcher_method.inspect}, @target, &block) + matcher.__send__(matcher_method, @target, &block) end - RUBY end def self.disallow_negation(method) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 9e4a50466..9f9cd87a2 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -265,7 +265,7 @@ def eq(value) framework.new.instance_eval do expect(3).to eq(3) end - }.to raise_error(/only the `receive` matcher is supported/) + }.to raise_error(/only the `receive` matcher is supported with `expect\(...\).to`/) end it 'can toggle the available syntax' do From d3ef19ccd2c0edef5fbd9b8035c6e24f873fad59 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Thu, 4 Jul 2013 16:37:45 -0400 Subject: [PATCH 0089/1179] Remove string eval and replace with class_exec. These two methods are particularly tricky in their use of string interpolation for the `class_eval`. To remove the string passed to `class_eval` one of two approaches is possible: * Retain the `class_eval` and exploit the block closure by defining local variables referencing the instance variables to be used in the eval block * Switch to using `class_exec` which was introduced in 1.8.7 and 1.9. This allows for instance variables to be passed into the block creating block local variables I choose the latter for writing cleaner code and to embrace the future dropping of support for 1.8.6. --- lib/rspec/mocks/method_double.rb | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 268e77ba9..b444deb0e 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -160,7 +160,7 @@ class << @object; self; end # @private def configure_method - @original_visibility = visibility_for_method + @original_visibility = [visibility, method_name] @method_stasher.stash unless @method_is_proxied define_proxy_method end @@ -169,20 +169,15 @@ def configure_method def define_proxy_method return if @method_is_proxied - object_singleton_class.class_eval <<-EOF, __FILE__, __LINE__ + 1 - def #{@method_name}(*args, &block) - ::RSpec::Mocks.proxy_for(self).message_received :#{@method_name}, *args, &block + object_singleton_class.class_exec(method_name, visibility) do |method_name, visibility| + define_method(method_name) do |*args, &block| + ::RSpec::Mocks.proxy_for(self).message_received method_name, *args, &block end - #{visibility_for_method} - EOF + self.__send__ visibility, method_name + end @method_is_proxied = true end - # @private - def visibility_for_method - "#{visibility} :#{method_name}" - end - # @private def restore_original_method return unless @method_is_proxied @@ -197,7 +192,9 @@ def restore_original_method # @private def restore_original_visibility return unless object_singleton_class.method_defined?(@method_name) || object_singleton_class.private_method_defined?(@method_name) - object_singleton_class.class_eval(@original_visibility, __FILE__, __LINE__) + object_singleton_class.class_exec(*@original_visibility) do |visibility, method_name| + self.__send__ visibility, method_name + end end # @private From 19eb8716e9ff0e7e7380daa44a2dc991b602f12d Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 Jul 2013 19:53:52 +0200 Subject: [PATCH 0090/1179] Remove mock and stub style doubles. Signed-off-by: Sam Phippen --- Changelog.md | 2 ++ features/outside_rspec/configuration.feature | 2 -- lib/rspec/mocks/example_methods.rb | 14 -------------- spec/rspec/mocks/double_spec.rb | 12 ------------ 4 files changed, 2 insertions(+), 28 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7a4b7548c..965a21678 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,8 @@ Breaking Changes for 3.0.0: Phippen) * Remove 1.8.6 workarounds (Jon Rowe) * Remove `stub!` and `unstub!`. (Sam Phippen) +* Remove `mock(name, methods)` and `stub(name, methods)`, + leaving `double(name, methods)` for creating test doubles. (Sam Phippen) Enhancements: diff --git a/features/outside_rspec/configuration.feature b/features/outside_rspec/configuration.feature index 5ba3c383f..b82f8578b 100644 --- a/features/outside_rspec/configuration.feature +++ b/features/outside_rspec/configuration.feature @@ -34,8 +34,6 @@ Feature: configure any test framework to use rspec-mocks example.init puts example.respond_to?(:double) - puts example.respond_to?(:mock) - puts example.respond_to?(:stub) """ When I run `ruby foo.rb` diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index d49f5bf9d..cbe10f156 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -25,24 +25,10 @@ module ExampleMethods # card.suit #=> "Spades" # card.rank #=> "A" # - # @see #mock - # @see #stub def double(*args) declare_double('Double', *args) end - # Deprecated: Use [double](#double-instance_method). - def mock(*args) - RSpec.deprecate "mock", :replacement => "double" - declare_double('Mock', *args) - end - - # Deprecated: Use [double](#double-instance_method). - def stub(*args) - RSpec.deprecate "stub", :replacement => "double" - declare_double('Stub', *args) - end - # Disables warning messages about expectations being set on nil. # # By default warning messages are issued when expectations are set on diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 749a0338a..c9d5ec1cc 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -9,16 +9,4 @@ double = double('name') expect {double.foo}.to raise_error(/Double "name" received/) end - - describe "deprecated aliases" do - it "warns if #stub is used" do - expect(RSpec).to receive(:deprecate).with("stub", :replacement => "double") - stub("TestDouble") - end - - it "warns if #mock is used" do - expect(RSpec).to receive(:deprecate).with("mock", :replacement => "double") - mock("TestDouble") - end - end end From 7569e3b271f00a0253f037379645b3acd34549ca Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Thu, 4 Jul 2013 20:46:02 -0400 Subject: [PATCH 0091/1179] Flatten class_eval into define_methods messages. Remove the instances where `class_eval` is there only to change the value of `self` for the `define_method` message. Just send it directly to the desired object. Use `__send__` as `define_method` is private. Remove the superfluous instances of `to_syms`. --- lib/rspec/mocks/any_instance/recorder.rb | 22 ++++++++-------------- lib/rspec/mocks/method_double.rb | 4 +--- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 466a2b361..7f15b63a1 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -180,25 +180,19 @@ def observe!(method_name) stop_observing!(method_name) if already_observing?(method_name) @observed_methods << method_name backup_method!(method_name) - @klass.class_eval do - define_method(method_name) do |*args, &blk| - method_name = method_name.to_sym - klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner - ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name) - self.__send__(method_name.to_sym, *args, &blk) - end + @klass.__send__(:define_method, method_name) do |*args, &blk| + klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner + ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name) + self.__send__(method_name, *args, &blk) end end def mark_invoked!(method_name) backup_method!(method_name) - @klass.class_eval do - define_method(method_name) do |*args, &blk| - method_name = method_name.to_sym - klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner - invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name) - raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{self.inspect} but has already been received by #{invoked_instance}" - end + @klass.__send__(:define_method, method_name) do |*args, &blk| + klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner + invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name) + raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{self.inspect} but has already been received by #{invoked_instance}" end end end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index b444deb0e..45c0bcae1 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -192,9 +192,7 @@ def restore_original_method # @private def restore_original_visibility return unless object_singleton_class.method_defined?(@method_name) || object_singleton_class.private_method_defined?(@method_name) - object_singleton_class.class_exec(*@original_visibility) do |visibility, method_name| - self.__send__ visibility, method_name - end + object_singleton_class.__send__(*@original_visibility) end # @private From 63fd8c46436adfb9defadcc1f1ac6f51b9688391 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Fri, 5 Jul 2013 08:29:50 -0400 Subject: [PATCH 0092/1179] Remove extra to_sym. --- lib/rspec/mocks/any_instance/chain.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 6168199b2..11c6bc61e 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -17,7 +17,7 @@ module Customizations # def self.record(method_name) define_method(method_name) do |*args, &block| - record(method_name.to_sym, *args, &block) + record(method_name, *args, &block) end end From e12d2793afc04e2f6aba6c808d5a0b9b5895efff Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 Jul 2013 20:09:23 +0200 Subject: [PATCH 0093/1179] Remove `any_number_of_times`. Signed-off-by: Sam Phippen --- Changelog.md | 2 + lib/rspec/mocks/message_expectation.rb | 9 ----- spec/rspec/mocks/any_instance_spec.rb | 38 ------------------- spec/rspec/mocks/any_number_of_times_spec.rb | 33 +--------------- spec/rspec/mocks/block_return_value_spec.rb | 17 --------- ...bining_implementation_instructions_spec.rb | 6 --- spec/rspec/mocks/mock_spec.rb | 10 ----- 7 files changed, 4 insertions(+), 111 deletions(-) diff --git a/Changelog.md b/Changelog.md index 965a21678..84293d3ed 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,8 @@ Breaking Changes for 3.0.0: * Remove `stub!` and `unstub!`. (Sam Phippen) * Remove `mock(name, methods)` and `stub(name, methods)`, leaving `double(name, methods)` for creating test doubles. (Sam Phippen) +* Remove `any_number_of_times` since `should_receive(:msg).any_number_of_times` + is really a stub in a mock's clothing. (Sam Phippen) Enhancements: diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 3e5c2f3ef..b6b9a939e 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -361,15 +361,6 @@ def times(&block) self end - - # Allows an expected message to be received any number of times. - def any_number_of_times(&block) - RSpec.deprecate "any_number_of_times", :replacement => "stub" - self.inner_implementation_action = block - @expected_received_count = :any - self - end - # Expect a message not to be received at all. # # @example diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index e0dad4e10..555357b63 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -681,44 +681,6 @@ def foo; end end end end - - context "the 'any_number_of_times' constraint" do - it "passes for 0 invocations" do - klass.any_instance.should_receive(:foo).any_number_of_times - verify klass.new - end - - it "passes for a non-zero number of invocations" do - allow(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") - - klass.any_instance.should_receive(:foo).any_number_of_times - instance = klass.new - instance.foo - verify instance - end - - it "does not interfere with other expectations" do - klass.any_instance.should_receive(:foo).any_number_of_times - klass.any_instance.should_receive(:existing_method).and_return(5) - expect(klass.new.existing_method).to eq(5) - end - - context "when combined with other expectations" do - it "passes when the other expecations are met" do - klass.any_instance.should_receive(:foo).any_number_of_times - klass.any_instance.should_receive(:existing_method).and_return(5) - expect(klass.new.existing_method).to eq(5) - end - - it "fails when the other expecations are not met" do - expect do - klass.any_instance.should_receive(:foo).any_number_of_times - klass.any_instance.should_receive(:existing_method).and_return(5) - RSpec::Mocks.space.verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) - end - end - end end end diff --git a/spec/rspec/mocks/any_number_of_times_spec.rb b/spec/rspec/mocks/any_number_of_times_spec.rb index 5b0745f12..c28760358 100644 --- a/spec/rspec/mocks/any_number_of_times_spec.rb +++ b/spec/rspec/mocks/any_number_of_times_spec.rb @@ -1,36 +1,7 @@ require 'spec_helper' describe "AnyNumberOfTimes" do - before(:each) do - @mock = RSpec::Mocks::Mock.new("test mock") - allow(RSpec).to receive(:deprecate) - end - - it "is deprecated" do - expect(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") - expect(@mock).to receive(:random_call).any_number_of_times - end - - it "passes if any number of times method is called many times" do - @mock.should_receive(:random_call).any_number_of_times - (1..10).each do - @mock.random_call - end - end - - it "passes if any number of times method is called once" do - @mock.should_receive(:random_call).any_number_of_times - @mock.random_call - end - - it "passes if any number of times method is not called" do - @mock.should_receive(:random_call).any_number_of_times - end - - it "returns the mocked value when called after a similar stub" do - @mock.stub(:message).and_return :stub_value - @mock.should_receive(:message).any_number_of_times.and_return(:mock_value) - expect(@mock.message).to eq :mock_value - expect(@mock.message).to eq :mock_value + it "is gone" do + expect(RSpec::Mocks::MessageExpectation.instance_methods).not_to include(:any_number_of_times) end end diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 8799174d6..92bfac369 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -43,23 +43,6 @@ end end - describe 'any_number_of_times' do - before do - RSpec.stub(:deprecate) - end - - it "warns about deprecation" do - expect(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") - Object.new.stub(:foo).any_number_of_times { 'bar' } - end - - it "returns the value of executing the block" do - obj = Object.new - obj.stub(:foo).any_number_of_times { 'bar' } - expect(obj.foo).to eq('bar') - end - end - describe "times" do it "returns the value of executing the block" do obj = Object.new diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 64c3450ad..f00132b1f 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -60,12 +60,6 @@ def verify_combined_implementation end end - it 'works when passing a block to `any_number_of_times`' do - verify_combined_implementation do |dbl| - dbl.should_receive(:foo).any_number_of_times { @block_called = true } - end - end - it 'works when passing a block to `once`' do verify_combined_implementation do |dbl| dbl.should_receive(:foo).once { @block_called = true } diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index feddf6ba3..6f35e4ae3 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -635,16 +635,6 @@ def add_call expect(@calls).to eq 10 end - it "calls the block after #any_number_of_times" do - expect(RSpec).to receive(:deprecate).with("any_number_of_times", :replacement => "stub") - - @double.should_receive(:foo).any_number_of_times { add_call } - - (1..7).each { @double.foo } - - expect(@calls).to eq 7 - end - it "calls the block after #ordered" do @double.should_receive(:foo).ordered { add_call } @double.should_receive(:bar).ordered { add_call } From 486708ef7cdda8d3a4b61a7e03d366cb291ce417 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 5 Jul 2013 02:03:10 +0200 Subject: [PATCH 0094/1179] Remove any_number_of_times_spec now that the method is gone. --- spec/rspec/mocks/any_number_of_times_spec.rb | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 spec/rspec/mocks/any_number_of_times_spec.rb diff --git a/spec/rspec/mocks/any_number_of_times_spec.rb b/spec/rspec/mocks/any_number_of_times_spec.rb deleted file mode 100644 index c28760358..000000000 --- a/spec/rspec/mocks/any_number_of_times_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'spec_helper' - -describe "AnyNumberOfTimes" do - it "is gone" do - expect(RSpec::Mocks::MessageExpectation.instance_methods).not_to include(:any_number_of_times) - end -end From d5b1ec32205a005ceb1d62f159f634ee9cac20bc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 6 Jul 2013 08:52:31 -0700 Subject: [PATCH 0095/1179] Reword a couple of confusing changelog entries. [ci skip] --- Changelog.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 84293d3ed..da844bebe 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,13 +15,15 @@ Enhancements: * Document test spies in the readme. (Adarsh Pandit) * Add an `array_including` matcher. (Sam Phippen) -* Create syntax agnostic message matchers enable message allowances and - expectations to be set independently of any particular syntax being - enabled (Paul Annesley, Myron Marston and Sam Phippen). +* Add a syntax-agnostic API for mocking or stubbing a method. This is + intended for use by libraries such as rspec-rails that need to mock + or stub a method, and work regardless of the syntax the user has + configured (Paul Annesley, Myron Marston and Sam Phippen). Bug Fixes: -* Bypass RSpec::Mocks::Syntax when mass-assigning stubs via double(). (Paul Annesley) +* Fix `double` so that it sets up passed stubs correctly regardless of + the configured syntax (Paul Annesley). * Allow a block implementation to be used in combination with `and_yield`, `and_raise`, `and_return` or `and_throw`. This got fixed in 2.13.1 but failed to get merged into master for the 2.14.0.rc1 From 50b7c6ae48a167621612d4df34aae95aeaddcd9b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 6 Jul 2013 09:06:34 -0700 Subject: [PATCH 0096/1179] Use new `allow_message` API. --- lib/rspec/mocks/test_double.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index a2509896a..f5455bb0c 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -110,8 +110,7 @@ def extract_option(source, target, key, default=nil) def assign_stubs(stubs) stubs.each_pair do |message, response| - matcher = Matchers::Receive.new(message, nil).and_return(response) - matcher.setup_allowance(self) + Mocks.allow_message(self, message).and_return(response) end end From 7945ba5e39737c981ae79a5efa7955ba56ce91f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 6 Jul 2013 18:17:52 -0700 Subject: [PATCH 0097/1179] Update changelog to reflect 2.14.0 release. [ci skip] --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index da844bebe..abe22a339 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,5 @@ ### Development +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...master) Breaking Changes for 3.0.0: @@ -11,6 +12,9 @@ Breaking Changes for 3.0.0: * Remove `any_number_of_times` since `should_receive(:msg).any_number_of_times` is really a stub in a mock's clothing. (Sam Phippen) +### 2.14.0 / 2013-07-06 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0.rc1...v2.14.0) + Enhancements: * Document test spies in the readme. (Adarsh Pandit) From d6c88c55aaf7ea0bdcd30b5b4c1682856cab3a5b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 6 Jul 2013 18:18:10 -0700 Subject: [PATCH 0098/1179] Master is 3.0.0.pre now. [ci skip] --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index b0f6c46b3..12b02b92b 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module Version - STRING = '2.14.0.rc1' + STRING = '3.0.0.pre' end end end From 6287f7ad0f657e7d22eaf71dd97926952624e7d3 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 7 Jul 2013 13:10:00 +0100 Subject: [PATCH 0099/1179] Pass the instance to any instance stubs. Signed-off-by: Sam Phippen --- lib/rspec/mocks/any_instance/recorder.rb | 7 ++- lib/rspec/mocks/configuration.rb | 8 ++++ spec/rspec/mocks/any_instance_spec.rb | 58 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 2f3bc095b..c390cf856 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -55,7 +55,7 @@ def stub_chain(*method_names_and_optional_return_values, &block) # @see Methods#should_receive def should_receive(method_name, &block) @expectation_set = true - observe!(method_name) + observe!(method_name, true) message_chains.add(method_name, PositiveExpectationChain.new(method_name, &block)) end @@ -166,13 +166,16 @@ def stop_observing!(method_name) @observed_methods.delete(method_name) end - def observe!(method_name) + def observe!(method_name, ignore_instance=false) stop_observing!(method_name) if already_observing?(method_name) @observed_methods << method_name backup_method!(method_name) @klass.__send__(:define_method, method_name) do |*args, &blk| klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name) + if !ignore_instance && ::RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + args.unshift(self) + end self.__send__(method_name, *args, &blk) end end diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 4e5cd8cc4..23a26b3b1 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -23,6 +23,14 @@ def add_stub_and_should_receive_to(*modules) end end + def pass_instance_to_any_instance_stubs + @pass_instance_to_any_instance_stubs ||= false + end + + def pass_instance_to_any_instance_stubs=(arg) + @pass_instance_to_any_instance_stubs = arg + end + def syntax=(values) if Array(values).include?(:expect) Syntax.enable_expect diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 555357b63..fdde6c13e 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -830,6 +830,64 @@ def foo; end end end + context "passing self" do + context "when configured to pass the instance" do + before(:each) do + @orig_pass = RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs = true + end + + after(:each) do + RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs = @orig_pass + end + + describe "an any instance stub" do + it "receives the instance" do + klass = Struct.new(:science) + instance = klass.new + klass.any_instance.stub(:bees) { |*args| expect(args.first).to eq(instance) } + instance.bees + end + end + + describe "an any instance expectation" do + it "doesn't effect with" do + klass = Struct.new(:science) + instance = klass.new + klass.any_instance.should_receive(:bees).with(:sup) + instance.bees(:sup) + end + + it "does not pass the instance" do + klass = Struct.new(:science) + instance = klass.new + klass.any_instance.should_receive(:bees).with(:sup) { |*args| expect(args.first).to eq(:sup) } + instance.bees(:sup) + end + end + end + + context "when configured not to pass the instance" do + before(:each) do + @orig_pass = RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs = false + end + + after(:each) do + RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs = @orig_pass + end + + describe "an any instance stub" do + it "does not receive the instance" do + klass = Struct.new(:science) + instance = klass.new + klass.any_instance.stub(:bees) { |*args| expect(args).to be_empty } + instance.bees + end + end + end + end + context 'when used in conjunction with a `dup`' do it "doesn't cause an infinite loop" do pending "This intermittently fails on JRuby" if RUBY_PLATFORM == 'java' From a37e021716b766432b5d854584fdd43349c2b388 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 7 Jul 2013 13:34:09 +0100 Subject: [PATCH 0100/1179] Pass the instance to should_receive matchers on any instance too. Signed-off-by: Sam Phippen --- lib/rspec/mocks/any_instance/expectation_chain.rb | 5 +++++ lib/rspec/mocks/any_instance/recorder.rb | 2 +- lib/rspec/mocks/message_expectation.rb | 5 +++++ spec/rspec/mocks/any_instance_spec.rb | 11 +++++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 699a2153b..d6dc8d54e 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -25,6 +25,11 @@ class PositiveExpectationChain < ExpectationChain def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.proxy_for(instance) expected_from = IGNORED_BACKTRACE_LINE + if @expectation_args.last.is_a? Hash + @expectation_args.last[:is_any_instance_expectation] = true + else + @expectation_args << {:is_any_instance_expectation => true} + end proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block) end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index c390cf856..7a1bf3799 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -173,7 +173,7 @@ def observe!(method_name, ignore_instance=false) @klass.__send__(:define_method, method_name) do |*args, &blk| klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name) - if !ignore_instance && ::RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + if ::RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs args.unshift(self) end self.__send__(method_name, *args, &blk) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index b6b9a939e..1f0f0f967 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -24,6 +24,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @args_to_yield = [] @failed_fast = nil @eval_context = nil + @is_any_instance_expectation = opts[:is_any_instance_expectation] @implementation = Implementation.new self.inner_implementation_action = implementation_block @@ -168,6 +169,10 @@ def and_yield(*args, &block) # @private def matches?(message, *args) + args = args.dup + if @is_any_instance_expectation && ::RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + args.shift + end @message == message && @argument_list_matcher.args_match?(*args) end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index fdde6c13e..ec9ed3371 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -858,10 +858,10 @@ def foo; end instance.bees(:sup) end - it "does not pass the instance" do + it "passes the instance" do klass = Struct.new(:science) instance = klass.new - klass.any_instance.should_receive(:bees).with(:sup) { |*args| expect(args.first).to eq(:sup) } + klass.any_instance.should_receive(:bees).with(:sup) { |*args| expect(args.first).to eq(instance) } instance.bees(:sup) end end @@ -884,6 +884,13 @@ def foo; end klass.any_instance.stub(:bees) { |*args| expect(args).to be_empty } instance.bees end + + it "gets data from with correctly" do + klass = Struct.new(:science) + instance = klass.new + klass.any_instance.should_receive(:bees).with(:faces) + instance.bees(:faces) + end end end end From 4cb7c1a220ebeda50de0a8fa829b4bdcc2cf76b2 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 7 Jul 2013 13:40:49 +0100 Subject: [PATCH 0101/1179] Remove the unused ignore_instance parameter. Signed-off-by: Sam Phippen --- lib/rspec/mocks/any_instance/recorder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 7a1bf3799..4af7e648d 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -55,7 +55,7 @@ def stub_chain(*method_names_and_optional_return_values, &block) # @see Methods#should_receive def should_receive(method_name, &block) @expectation_set = true - observe!(method_name, true) + observe!(method_name) message_chains.add(method_name, PositiveExpectationChain.new(method_name, &block)) end @@ -166,7 +166,7 @@ def stop_observing!(method_name) @observed_methods.delete(method_name) end - def observe!(method_name, ignore_instance=false) + def observe!(method_name) stop_observing!(method_name) if already_observing?(method_name) @observed_methods << method_name backup_method!(method_name) From 825e88b975947e809a5153055d98ebe39cac4b76 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 7 Jul 2013 15:38:15 -0400 Subject: [PATCH 0102/1179] Works around Rubinius bug --- features/support/rubinius.rb | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 features/support/rubinius.rb diff --git a/features/support/rubinius.rb b/features/support/rubinius.rb new file mode 100644 index 000000000..390796252 --- /dev/null +++ b/features/support/rubinius.rb @@ -0,0 +1,6 @@ +# Required until https://github.com/rubinius/rubinius/issues/2430 is resolved +ENV['RBXOPT'] = "#{ENV["RBXOPT"]} -Xcompiler.no_rbc" + +Around "@unsupported-on-rbx" do |scenario, block| + block.call unless defined?(Rubinius) +end From 8d6eb5d2e527832f91e747863fcff32bc378d40f Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 7 Jul 2013 16:11:40 -0400 Subject: [PATCH 0103/1179] rbx build is green; let us try to keep it that way --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index e33f390e3..17ab03034 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,3 @@ rvm: - rbx-18mode - rbx-19mode - 2.0.0 -matrix: - allow_failures: - - rvm: rbx-18mode - - rvm: rbx-19mode - From 372346aba6554512885583ae1f00e8d4db21ec47 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 7 Jul 2013 15:27:11 -0700 Subject: [PATCH 0104/1179] Add missing 2.13.1 release notes. [ci skip] --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index abe22a339..a7ec62b7c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -86,6 +86,14 @@ Deprecations (Jon Rowe). * Deprecate `at_least(0).times` and `any_number_of_times` (Michi Huber). +### 2.13.1 / 2013-04-06 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.13.1) + +Bug fixes + +* Allow a block implementation to be used in combination with + `and_yield`, `and_raise`, `and_return` or `and_throw` (Myron Marston). + ### 2.13.0 / 2013-02-23 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.2...v2.13.0) From a02018da83348799d4b7431fb9b454a609811146 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 7 Jul 2013 16:09:49 -0700 Subject: [PATCH 0105/1179] Add spec documenting the fact that null-ness doesn't persist between examples. --- Changelog.md | 3 +++ spec/rspec/mocks/null_object_mock_spec.rb | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/Changelog.md b/Changelog.md index a7ec62b7c..e3a3f4224 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,9 @@ Breaking Changes for 3.0.0: leaving `double(name, methods)` for creating test doubles. (Sam Phippen) * Remove `any_number_of_times` since `should_receive(:msg).any_number_of_times` is really a stub in a mock's clothing. (Sam Phippen) +* Remove support for re-using the same null-object test double in multiple + examples. Test doubles are designed to only live for one example. + (Myron Marston) ### 2.14.0 / 2013-07-06 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0.rc1...v2.14.0) diff --git a/spec/rspec/mocks/null_object_mock_spec.rb b/spec/rspec/mocks/null_object_mock_spec.rb index cd849234e..b67b3c686 100644 --- a/spec/rspec/mocks/null_object_mock_spec.rb +++ b/spec/rspec/mocks/null_object_mock_spec.rb @@ -88,6 +88,17 @@ module Mocks # returns an int value from #to_int. expect(("%i" % @double)).to eq("0") end + + it "does not allow null-ness to persist between examples" do + RSpec::Mocks.teardown + + expect(@double).not_to be_null_object + expect { @double.some.long.message.chain }.to raise_error(RSpec::Mocks::MockExpectationError) + + @double.as_null_object + expect(@double).to be_null_object + expect { @double.some.long.message.chain }.not_to raise_error + end end describe "#as_null_object" do From cd3341a073d5e4619f7111ec0924c74849a09e09 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 7 Jul 2013 20:42:29 -0400 Subject: [PATCH 0106/1179] Revert "rbx build is green; let us try to keep it that way" This reverts commit 8d6eb5d2e527832f91e747863fcff32bc378d40f. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 17ab03034..e33f390e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,3 +12,8 @@ rvm: - rbx-18mode - rbx-19mode - 2.0.0 +matrix: + allow_failures: + - rvm: rbx-18mode + - rvm: rbx-19mode + From c7eb20e9c47373c94da1c874800aca96fc3cd933 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 7 Jul 2013 20:09:54 -0700 Subject: [PATCH 0107/1179] Update changelog with 2.14.1 release notes. [ci skip] --- Changelog.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Changelog.md b/Changelog.md index e3a3f4224..2063f0389 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,18 @@ Breaking Changes for 3.0.0: examples. Test doubles are designed to only live for one example. (Myron Marston) +### 2.14.1 / 2013-07-07 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) + +Bug Fixes: + +* Restore `double.as_null_object` behavior from 2.13 and earlier: a + double's nullness persisted between examples in earlier examples. + While this is not an intended use case (test doubles are meant to live + for only one example), we don't want to break behavior users rely + on in a minor relase. This will be deprecated in 2.99 and removed + in 3.0. (Myron Marston) + ### 2.14.0 / 2013-07-06 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0.rc1...v2.14.0) From f7caa59b20263a5445aef483a0494da6fa62230b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 10 Jul 2013 20:45:30 +1000 Subject: [PATCH 0108/1179] ensure null objects behave correctly with to_a and to_ary --- features/method_stubs/to_ary.feature | 12 ++++++++---- lib/rspec/mocks/test_double.rb | 7 +++++-- spec/rspec/mocks/mock_spec.rb | 10 ++++++++++ spec/rspec/mocks/to_ary_spec.rb | 14 ++++++++------ 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/features/method_stubs/to_ary.feature b/features/method_stubs/to_ary.feature index 8fa8d54aa..251fa5054 100644 --- a/features/method_stubs/to_ary.feature +++ b/features/method_stubs/to_ary.feature @@ -17,10 +17,6 @@ Feature: double handling to_ary """ruby describe "#to_ary" do shared_examples "to_ary" do - it "raises a NoMethodError" do - expect { obj.to_ary }.to raise_error(NoMethodError) - end - it "can be overridden with a stub" do obj.stub(:to_ary) { :non_nil_value } obj.to_ary.should be(:non_nil_value) @@ -35,11 +31,19 @@ Feature: double handling to_ary context "sent to a double as_null_object" do let(:obj) { double('obj').as_null_object } include_examples "to_ary" + + it "returns nil" do + expect( obj.to_ary ).to eq nil + end end context "sent to a double without as_null_object" do let(:obj) { double('obj') } include_examples "to_ary" + + it "raises a NoMethodError" do + expect { obj.to_ary }.to raise_error(NoMethodError) + end end end """ diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index f5455bb0c..332ca3d64 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -79,13 +79,16 @@ def __initialize_as_test_double(name=nil, stubs_and_options={}) end def method_missing(message, *args, &block) - raise NoMethodError if message == :to_ary || message == :to_a - return 0 if message == :to_int && __mock_proxy.null_object? + if __mock_proxy.null_object? + return 0 if message == :to_int + return nil if [:to_a,:to_ary].include? message + end __mock_proxy.record_message_received(message, *args, &block) begin __mock_proxy.null_object? ? self : super rescue NameError + raise NoMethodError if message == :to_ary || message == :to_a __mock_proxy.raise_unexpected_message_error(message, *args) end end diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 6f35e4ae3..9007f2fd1 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -217,6 +217,16 @@ def @double.method_with_default_argument(arg={}); end expect( Array(@double) ).to eq([@double]) end + it "is wrappable in an array when a null object" do + expect( Array(@double.as_null_object) ).to eq [@double] + end + + %w[to_ary to_a].each do |method| + it "responds to #{method} as a null object" do + expect(@double.as_null_object.send method).to eq nil + end + end + it "passes proc to expectation block without an argument" do @double.should_receive(:foo) { |&block| expect(block.call).to eq(:bar) } @double.foo { :bar } diff --git a/spec/rspec/mocks/to_ary_spec.rb b/spec/rspec/mocks/to_ary_spec.rb index dce0cbd3f..b448ea8c7 100644 --- a/spec/rspec/mocks/to_ary_spec.rb +++ b/spec/rspec/mocks/to_ary_spec.rb @@ -2,12 +2,6 @@ describe "a double receiving to_ary" do shared_examples "to_ary" do - it "returns nil" do - expect do - expect(obj.to_ary).to be_nil - end.to raise_error(NoMethodError) - end - it "doesn't respond" do expect(obj).not_to respond_to(:to_ary) end @@ -31,10 +25,18 @@ context "double as_null_object" do let(:obj) { double('obj').as_null_object } include_examples "to_ary" + + it "returns nil" do + expect(obj.to_ary).to eq nil + end end context "double without as_null_object" do let(:obj) { double('obj') } include_examples "to_ary" + + it "raises NoMethodError" do + expect { obj.to_ary }.to raise_error(NoMethodError) + end end end From 3efca73027c0860e3d07ba6b11996355702aa3ba Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 11 Jul 2013 07:59:06 +1000 Subject: [PATCH 0109/1179] 1.8.7 has a default implementation on top --- lib/rspec/mocks/test_double.rb | 5 +++-- spec/rspec/mocks/mock_spec.rb | 12 +++++++++--- spec/rspec/mocks/to_ary_spec.rb | 22 +++++++++++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 332ca3d64..9f3c32ff2 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -57,7 +57,7 @@ def to_s # @private def respond_to?(message, incl_private=false) - __mock_proxy.null_object? && message != :to_ary ? true : super + __mock_proxy.null_object? ? true : super end # @private @@ -88,7 +88,8 @@ def method_missing(message, *args, &block) begin __mock_proxy.null_object? ? self : super rescue NameError - raise NoMethodError if message == :to_ary || message == :to_a + # for 1.9.2 + raise NoMethodError if [:to_a, :to_ary].include? message __mock_proxy.raise_unexpected_message_error(message, *args) end end diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 9007f2fd1..1a504ba46 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -221,9 +221,15 @@ def @double.method_with_default_argument(arg={}); end expect( Array(@double.as_null_object) ).to eq [@double] end - %w[to_ary to_a].each do |method| - it "responds to #{method} as a null object" do - expect(@double.as_null_object.send method).to eq nil + it "responds to to_ary as a null object" do + expect(@double.as_null_object.to_ary).to eq nil + end + + it "responds to to_a as a null object" do + if RUBY_VERSION.to_f > 1.8 + expect(@double.as_null_object.to_a).to eq nil + else + expect(@double.as_null_object.to_a).to eq [@double] end end diff --git a/spec/rspec/mocks/to_ary_spec.rb b/spec/rspec/mocks/to_ary_spec.rb index b448ea8c7..c1d2810d9 100644 --- a/spec/rspec/mocks/to_ary_spec.rb +++ b/spec/rspec/mocks/to_ary_spec.rb @@ -2,10 +2,6 @@ describe "a double receiving to_ary" do shared_examples "to_ary" do - it "doesn't respond" do - expect(obj).not_to respond_to(:to_ary) - end - it "can be overridden with a stub" do obj.stub(:to_ary) { :non_nil_value } expect(obj.to_ary).to be(:non_nil_value) @@ -26,6 +22,14 @@ let(:obj) { double('obj').as_null_object } include_examples "to_ary" + it "does respond to to_ary" do + expect(obj).to respond_to(:to_ary) + end + + it "does respond to to_a" do + expect(obj).to respond_to(:to_a) + end + it "returns nil" do expect(obj.to_ary).to eq nil end @@ -35,7 +39,15 @@ let(:obj) { double('obj') } include_examples "to_ary" - it "raises NoMethodError" do + it "doesn't respond to to_ary" do + expect(obj).not_to respond_to(:to_ary) + end + + it "doesn't respond to to_a", :if => ( RUBY_VERSION.to_f > 1.8 ) do + expect(obj).not_to respond_to(:to_a) + end + + it "raises " do expect { obj.to_ary }.to raise_error(NoMethodError) end end From faa04cfb01e8145469099a245458b4551a3c265c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 11 Jul 2013 11:22:40 +1000 Subject: [PATCH 0110/1179] more descriptive comment --- lib/rspec/mocks/test_double.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 9f3c32ff2..8f9b6215c 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -88,7 +88,7 @@ def method_missing(message, *args, &block) begin __mock_proxy.null_object? ? self : super rescue NameError - # for 1.9.2 + # Required wrapping doubles in an Array on Ruby 1.9.2 raise NoMethodError if [:to_a, :to_ary].include? message __mock_proxy.raise_unexpected_message_error(message, *args) end From 71f39f7637ae342ec4198781f2c309ee5c121ac7 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 11 Jul 2013 12:33:56 +1000 Subject: [PATCH 0111/1179] refactor to case statement --- lib/rspec/mocks/test_double.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 8f9b6215c..b27e26c56 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -80,8 +80,10 @@ def __initialize_as_test_double(name=nil, stubs_and_options={}) def method_missing(message, *args, &block) if __mock_proxy.null_object? - return 0 if message == :to_int - return nil if [:to_a,:to_ary].include? message + case message + when :to_int then return 0 + when :to_a, :to_ary then return nil + end end __mock_proxy.record_message_received(message, *args, &block) From dc5b00f7403b5e05bafd867bc0876316f5484964 Mon Sep 17 00:00:00 2001 From: Federico Ravasio Date: Sun, 14 Jul 2013 05:46:18 -0700 Subject: [PATCH 0112/1179] Prolong aruba timeout to 60 even on Rubinius. --- features/support/env.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/features/support/env.rb b/features/support/env.rb index 89fe44636..bc02c7f11 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -2,7 +2,11 @@ require 'rspec/expectations' Before do - RUBY_PLATFORM =~ /java/ ? @aruba_timeout_seconds = 60 : @aruba_timeout_seconds = 5 + if RUBY_PLATFORM =~ /java/ || defined?(Rubinius) + @aruba_timeout_seconds = 60 + else + @aruba_timeout_seconds = 5 + end end Aruba.configure do |config| From ee33df7bcc3c4472c03d1552224be9a45eb4791a Mon Sep 17 00:00:00 2001 From: Federico Ravasio Date: Sun, 14 Jul 2013 05:46:47 -0700 Subject: [PATCH 0113/1179] Disable Rubinius' JIT when running aruba tests. --- features/support/env.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/features/support/env.rb b/features/support/env.rb index bc02c7f11..21420d497 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -14,3 +14,9 @@ set_env('JRUBY_OPTS', "-X-C #{ENV['JRUBY_OPTS']}") # disable JIT since these processes are so short lived end end if RUBY_PLATFORM == 'java' + +Aruba.configure do |config| + config.before_cmd do |cmd| + set_env('RBXOPT', "-Xint=true #{ENV['RBXOPT']}") # disable JIT since these processes are so short lived + end +end if defined?(Rubinius) From 638d3ffb9d21c25fe56c929fe7e3e91fba148e5f Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 14 Jul 2013 14:16:31 -0400 Subject: [PATCH 0114/1179] Fixes broken links in README [Closes #330] --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8d2d3cd5d..1407229c0 100644 --- a/README.md +++ b/README.md @@ -322,8 +322,8 @@ There are many different viewpoints about the meaning of mocks and stubs. If you are interested in learning more, here is some recommended reading: * Mock Objects: http://www.mockobjects.com/ -* Endo-Testing: http://www.mockobjects.com/files/endotesting.pdf -* Mock Roles, Not Objects: http://www.mockobjects.com/files/mockrolesnotobjects.pdf +* Endo-Testing: http://stalatest.googlecode.com/svn/trunk/Literatur/mockobjects.pdf +* Mock Roles, Not Objects: http://jmock.org/oopsla2004.pdf * Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html * Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html From 95476b124b1a15916b84ed52156a6a75d94f2a45 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 14 Jul 2013 19:49:31 +0100 Subject: [PATCH 0115/1179] Simpler implementation of yielding args for any instance calls. Signed-off-by: Sam Phippen --- lib/rspec/mocks/any_instance/expectation_chain.rb | 10 +++++----- lib/rspec/mocks/any_instance/recorder.rb | 3 --- lib/rspec/mocks/any_instance/stub_chain.rb | 8 +++++++- lib/rspec/mocks/message_expectation.rb | 13 +++++++++---- lib/rspec/mocks/proxy.rb | 6 ++++++ 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index d6dc8d54e..234d3a7a4 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -25,12 +25,12 @@ class PositiveExpectationChain < ExpectationChain def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.proxy_for(instance) expected_from = IGNORED_BACKTRACE_LINE - if @expectation_args.last.is_a? Hash - @expectation_args.last[:is_any_instance_expectation] = true - else - @expectation_args << {:is_any_instance_expectation => true} + me = proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block) + if RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + me.and_yield_receiver_to_implementation end - proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block) + + me end def invocation_order diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 4af7e648d..2f3bc095b 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -173,9 +173,6 @@ def observe!(method_name) @klass.__send__(:define_method, method_name) do |*args, &blk| klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name) - if ::RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs - args.unshift(self) - end self.__send__(method_name, *args, &blk) end end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index 9b1712a70..75be639d9 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -14,7 +14,13 @@ def expectation_fulfilled? def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.proxy_for(instance) expected_from = IGNORED_BACKTRACE_LINE - proxy.add_stub(expected_from, *@expectation_args, &@expectation_block) + stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block) + + if RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + stub.and_yield_receiver_to_implementation + end + + stub end def invocation_order diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 1f0f0f967..c3b80ac97 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -5,6 +5,8 @@ class MessageExpectation # @private attr_accessor :error_generator, :implementation attr_reader :message + attr_reader :orig_object + attr_reader :yield_receiver_to_implementation attr_writer :expected_received_count, :expected_from, :argument_list_matcher protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation= @@ -15,6 +17,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @error_generator.opts = opts @expected_from = expected_from @method_double = method_double + @orig_object = @method_double.object @message = @method_double.method_name @actual_received_count = 0 @expected_received_count = expected_received_count @@ -24,6 +27,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @args_to_yield = [] @failed_fast = nil @eval_context = nil + @yield_receiver_to_implementation = false @is_any_instance_expectation = opts[:is_any_instance_expectation] @implementation = Implementation.new @@ -90,6 +94,11 @@ def and_return(*values, &implementation) end end + def and_yield_receiver_to_implementation + @yield_receiver_to_implementation = true + self + end + # Tells the object to delegate to the original unmodified method # when it receives the message. # @@ -169,10 +178,6 @@ def and_yield(*args, &block) # @private def matches?(message, *args) - args = args.dup - if @is_any_instance_expectation && ::RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs - args.shift - end @message == message && @argument_list_matcher.args_match?(*args) end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 5dc63b40f..da5660635 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -139,8 +139,14 @@ def message_received(message, *args, &block) if expectation = find_almost_matching_expectation(message, *args) expectation.advise(*args) unless expectation.expected_messages_received? end + if stub.yield_receiver_to_implementation + args.unshift(stub.orig_object) + end stub.invoke(nil, *args, &block) elsif expectation + if expectation.yield_receiver_to_implementation + args.unshift(expectation.orig_object) + end expectation.invoke(stub, *args, &block) elsif expectation = find_almost_matching_expectation(message, *args) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? From ab4b965a3b81071dd9ab9c60e890110913139284 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Mon, 15 Jul 2013 10:43:45 -0400 Subject: [PATCH 0116/1179] Fully embrace _exec over _eval. --- lib/rspec/mocks.rb | 2 +- lib/rspec/mocks/any_instance/recorder.rb | 6 +++--- lib/rspec/mocks/example_methods.rb | 2 +- lib/rspec/mocks/space.rb | 2 +- lib/rspec/mocks/syntax.rb | 16 ++++++++-------- spec/rspec/mocks/and_call_original_spec.rb | 2 +- spec/rspec/mocks/matchers/receive_spec.rb | 14 +++++++------- spec/rspec/mocks/partial_mock_spec.rb | 2 +- spec/rspec/mocks/serialization_spec.rb | 8 ++++---- spec/rspec/mocks/test_double_spec.rb | 4 ++-- 10 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index ba724eac7..2591bbe75 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -8,7 +8,7 @@ class << self attr_accessor :space def setup(host) - (class << host; self; end).class_eval do + (class << host; self; end).class_exec do include RSpec::Mocks::ExampleMethods end self.space ||= RSpec::Mocks::Space.new diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 2f3bc095b..cd4730ae2 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -137,7 +137,7 @@ def restore_method!(method_name) def restore_original_method!(method_name) alias_method_name = build_alias_method_name(method_name) - @klass.class_eval do + @klass.class_exec do remove_method method_name alias_method method_name, alias_method_name remove_method alias_method_name @@ -145,14 +145,14 @@ def restore_original_method!(method_name) end def remove_dummy_method!(method_name) - @klass.class_eval do + @klass.class_exec do remove_method method_name end end def backup_method!(method_name) alias_method_name = build_alias_method_name(method_name) - @klass.class_eval do + @klass.class_exec do alias_method alias_method_name, method_name end if public_protected_or_private_method_defined?(method_name) end diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index cbe10f156..d10f5ac85 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -118,7 +118,7 @@ def have_received(method_name) end def self.included(klass) - klass.class_eval do + klass.class_exec do # This gets mixed in so that if `RSpec::Matchers` is included in # `klass` later, it's definition of `expect` will take precedence. include ExpectHost unless method_defined?(:expect) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index d8fdf2000..20b1e7b61 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -73,7 +73,7 @@ def id_for(object) return id if object.equal?(::ObjectSpace._id2ref(id)) # this suggests that object.__id__ is proxying through to some wrapped object - object.instance_eval do + object.instance_exec do @__id_for_rspec_mocks_space ||= ::SecureRandom.uuid end end diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index a1cefe1a8..25eadf73d 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -10,7 +10,7 @@ module Syntax def self.enable_should(syntax_host = default_should_syntax_host) return if should_enabled?(syntax_host) - syntax_host.class_eval do + syntax_host.class_exec do def should_receive(message, opts={}, &block) opts[:expected_from] ||= caller(1)[0] ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block) @@ -52,7 +52,7 @@ def received_message?(message, *args, &block) end unless Class.respond_to? :any_instance - Class.class_eval do + Class.class_exec do def any_instance ::RSpec::Mocks.any_instance_recorder_for(self) end @@ -66,7 +66,7 @@ def any_instance def self.disable_should(syntax_host = default_should_syntax_host) return unless should_enabled?(syntax_host) - syntax_host.class_eval do + syntax_host.class_exec do undef should_receive undef should_not_receive undef stub @@ -77,7 +77,7 @@ def self.disable_should(syntax_host = default_should_syntax_host) undef received_message? end - Class.class_eval do + Class.class_exec do undef any_instance end end @@ -87,7 +87,7 @@ def self.disable_should(syntax_host = default_should_syntax_host) def self.enable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods) return if expect_enabled?(syntax_host) - syntax_host.class_eval do + syntax_host.class_exec do def receive(method_name, &block) Matchers::Receive.new(method_name, block) end @@ -105,7 +105,7 @@ def allow_any_instance_of(klass) end end - RSpec::Mocks::ExampleMethods::ExpectHost.class_eval do + RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do def expect(target) ExpectationTarget.new(target) end @@ -117,14 +117,14 @@ def expect(target) def self.disable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods) return unless expect_enabled?(syntax_host) - syntax_host.class_eval do + syntax_host.class_exec do undef receive undef allow undef expect_any_instance_of undef allow_any_instance_of end - RSpec::Mocks::ExampleMethods::ExpectHost.class_eval do + RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do undef expect end end diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 3cd07f096..38f89e847 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -149,7 +149,7 @@ class << self context 'on an object that defines method_missing' do before do - klass.class_eval do + klass.class_exec do private def method_missing(name, *args) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 9f9cd87a2..8012ce938 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -209,7 +209,7 @@ def receiver.method_missing(*a); end # a poor man's stub... it 'can use the `expect` syntax' do dbl = double - framework.new.instance_eval do + framework.new.instance_exec do expect(dbl).to receive(:foo).and_return(3) end @@ -219,7 +219,7 @@ def receiver.method_missing(*a); end # a poor man's stub... it 'expects the method to be called when `expect` is used' do dbl = double - framework.new.instance_eval do + framework.new.instance_exec do expect(dbl).to receive(:foo) end @@ -229,7 +229,7 @@ def receiver.method_missing(*a); end # a poor man's stub... it 'supports `expect(...).not_to receive`' do dbl = double - framework.new.instance_eval do + framework.new.instance_exec do expect(dbl).not_to receive(:foo) end @@ -239,7 +239,7 @@ def receiver.method_missing(*a); end # a poor man's stub... it 'supports `expect(...).to_not receive`' do dbl = double - framework.new.instance_eval do + framework.new.instance_exec do expect(dbl).to_not receive(:foo) end @@ -262,7 +262,7 @@ def eq(value) it 'cannot use `expect` with another matcher' do expect { - framework.new.instance_eval do + framework.new.instance_exec do expect(3).to eq(3) end }.to raise_error(/only the `receive` matcher is supported with `expect\(...\).to`/) @@ -295,7 +295,7 @@ def eq(value) include_examples "using rspec-mocks in another test framework" it 'can use `expect` with any matcher' do - framework.new.instance_eval do + framework.new.instance_exec do expect(3).to eq(3) end end @@ -317,7 +317,7 @@ def eq(value) include_examples "using rspec-mocks in another test framework" it 'can use `expect` with any matcher' do - framework.new.instance_eval do + framework.new.instance_exec do expect(3).to eq(3) end end diff --git a/spec/rspec/mocks/partial_mock_spec.rb b/spec/rspec/mocks/partial_mock_spec.rb index e5bd34ade..acdeabf88 100644 --- a/spec/rspec/mocks/partial_mock_spec.rb +++ b/spec/rspec/mocks/partial_mock_spec.rb @@ -20,7 +20,7 @@ module Mocks end it "does not conflict with @options in the object" do - object.instance_eval { @options = Object.new } + object.instance_exec { @options = Object.new } object.should_receive(:blah) object.blah end diff --git a/spec/rspec/mocks/serialization_spec.rb b/spec/rspec/mocks/serialization_spec.rb index 530c68352..e8b637a04 100644 --- a/spec/rspec/mocks/serialization_spec.rb +++ b/spec/rspec/mocks/serialization_spec.rb @@ -8,7 +8,7 @@ class SerializableObject < Struct.new(:foo, :bar); end def self.with_yaml_loaded(&block) context 'with YAML loaded' do - module_eval(&block) + module_exec(&block) end end @@ -17,16 +17,16 @@ def self.without_yaml_loaded(&block) before do # We can't really unload yaml, but we can fake it here... hide_const("YAML") - Struct.class_eval do + Struct.class_exec do alias __old_to_yaml to_yaml undef to_yaml end end - module_eval(&block) + module_exec(&block) after do - Struct.class_eval do + Struct.class_exec do alias to_yaml __old_to_yaml undef __old_to_yaml end diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 7c16231cb..c396c5c9f 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -4,14 +4,14 @@ module RSpec module Mocks describe TestDouble do before(:all) do - Module.class_eval do + Module.class_exec do private def use; end end end after(:all) do - Module.class_eval do + Module.class_exec do undef use end end From 329074a3ec1b2ead6b465bafadb0dc96b8a6c0b6 Mon Sep 17 00:00:00 2001 From: Tim Cowlishaw Date: Tue, 16 Jul 2013 14:12:40 +0100 Subject: [PATCH 0117/1179] allow has_received to take a block to match against passed arguments to mocked method --- lib/rspec/mocks/example_methods.rb | 4 +- lib/rspec/mocks/matchers/have_received.rb | 8 ++-- lib/rspec/mocks/proxy.rb | 3 +- .../mocks/matchers/have_received_spec.rb | 40 +++++++++++++++++++ spec/rspec/mocks/matchers/receive_spec.rb | 36 +++++++++++++++++ 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index cbe10f156..0645235a8 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -113,8 +113,8 @@ def hide_const(constant_name) # # # You can also use most message expectations: # expect(invitation).to have_received(:accept).with(mailer).once - def have_received(method_name) - Matchers::HaveReceived.new(method_name) + def have_received(method_name, &block) + Matchers::HaveReceived.new(method_name, &block) end def self.included(klass) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 63cd1c3fa..4f8cfbac4 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -6,13 +6,15 @@ class HaveReceived ARGS_CONSTRAINTS = %w(with) CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS - def initialize(method_name) + def initialize(method_name, &block) @method_name = method_name + @block = block @constraints = [] @subject = nil end - def matches?(subject) + def matches?(subject, &block) + @block = block if block @subject = subject @expectation = expect expected_messages_received? @@ -79,7 +81,7 @@ def generate_failure_message end def expected_messages_received? - mock_proxy.replay_received_message_on @expectation + mock_proxy.replay_received_message_on @expectation, &@block @expectation.expected_messages_received? end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 5dc63b40f..68046bd09 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -62,7 +62,7 @@ def build_expectation(method_name) end # @private - def replay_received_message_on(expectation) + def replay_received_message_on(expectation, &block) expected_method_name = expectation.message meth_double = method_double[expected_method_name] @@ -77,6 +77,7 @@ def replay_received_message_on(expectation) @messages_received.each do |(actual_method_name, args, _)| if expectation.matches?(actual_method_name, *args) expectation.invoke(nil) + block.call(*args) if block end end end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 9a35a1c2d..a8938471c 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -48,6 +48,46 @@ module Mocks }.to raise_error(/method has been mocked instead of stubbed/) end + it "takes a curly-bracket block and yields the arguments given to the stubbed method call" do + dbl = double(:foo => nil) + yielded = [] + dbl.foo(:a, :b, :c) + expect(dbl).to have_received(:foo) { |*args| + yielded << args + } + expect(yielded).to include([:a,:b,:c]) + end + + it "takes a do-end block and yields the arguments given to the stubbed method call" do + dbl = double(:foo => nil) + yielded = [] + dbl.foo(:a, :b, :c) + expect(dbl).to have_received(:foo) do |*args| + yielded << args + end + expect(yielded).to include([:a,:b,:c]) + end + + it "passes if expectations against the yielded arguments pass" do + dbl = double(:foo => nil) + dbl.foo(42) + expect { + expect(dbl).to have_received(:foo) { |arg| + expect(arg).to eq(42) + } + }.to_not raise_error + end + + it "fails if expectations against the yielded arguments fail" do + dbl = double(:foo => nil) + dbl.foo(43) + expect { + expect(dbl).to have_received(:foo) { |arg| + expect(arg).to eq(42) + } + }.to raise_error(RSpec::Expectations::ExpectationNotMetError) + end + it 'resets expectations on class methods when mocks are reset' do dbl = Object dbl.stub(:expected_method) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 9f9cd87a2..bf641e12e 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -46,6 +46,42 @@ def verify_all expect(receiver.foo).to eq(:curly) end + it "takes a curly-bracket block and yields the arguments given to the stubbed method call" do + yielded = [] + wrapped.to receive(:foo) { |*a| + yielded << a + } + receiver.foo(:a, :b, :c) + expect(yielded).to include([:a,:b,:c]) + end + + it "takes a do-end block and yields the arguments given to the stubbed method call" do + yielded = [] + wrapped.to receive(:foo) do |*a| + yielded << a + end + receiver.foo(:a, :b, :c) + expect(yielded).to include([:a,:b,:c]) + end + + it "passes if expectations against the yielded arguments pass" do + expect { + wrapped.to receive(:foo) do |arg| + expect(arg).to eq(42) + end + receiver.foo(42) + }.to_not raise_error + end + + it "fails if expectations against the yielded arguments fail" do + expect { + wrapped.to receive(:foo) do |arg| + expect(arg).to eq(42) + end + receiver.foo(43) + }.to raise_error(RSpec::Expectations::ExpectationNotMetError) + end + it 'does not support other matchers', :unless => options.include?(:allow_other_matchers) do expect { wrapped.to eq(3) From 3e07edb9a7105ffecbd21588027630aa813b8de4 Mon Sep 17 00:00:00 2001 From: Tim Cowlishaw Date: Thu, 18 Jul 2013 07:36:20 +0100 Subject: [PATCH 0118/1179] remove unnecessary specs on receive matcher --- spec/rspec/mocks/matchers/receive_spec.rb | 36 ----------------------- 1 file changed, 36 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index bf641e12e..9f9cd87a2 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -46,42 +46,6 @@ def verify_all expect(receiver.foo).to eq(:curly) end - it "takes a curly-bracket block and yields the arguments given to the stubbed method call" do - yielded = [] - wrapped.to receive(:foo) { |*a| - yielded << a - } - receiver.foo(:a, :b, :c) - expect(yielded).to include([:a,:b,:c]) - end - - it "takes a do-end block and yields the arguments given to the stubbed method call" do - yielded = [] - wrapped.to receive(:foo) do |*a| - yielded << a - end - receiver.foo(:a, :b, :c) - expect(yielded).to include([:a,:b,:c]) - end - - it "passes if expectations against the yielded arguments pass" do - expect { - wrapped.to receive(:foo) do |arg| - expect(arg).to eq(42) - end - receiver.foo(42) - }.to_not raise_error - end - - it "fails if expectations against the yielded arguments fail" do - expect { - wrapped.to receive(:foo) do |arg| - expect(arg).to eq(42) - end - receiver.foo(43) - }.to raise_error(RSpec::Expectations::ExpectationNotMetError) - end - it 'does not support other matchers', :unless => options.include?(:allow_other_matchers) do expect { wrapped.to eq(3) From 59b3b3927cb158c6ae5baca1eb2cb9ccfbab42ee Mon Sep 17 00:00:00 2001 From: Tim Cowlishaw Date: Thu, 18 Jul 2013 07:43:55 +0100 Subject: [PATCH 0119/1179] ensure curly-brace block passed to have_received takes precedence over do-end --- lib/rspec/mocks/matchers/have_received.rb | 2 +- spec/rspec/mocks/matchers/have_received_spec.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 4f8cfbac4..7ffaedd27 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -14,7 +14,7 @@ def initialize(method_name, &block) end def matches?(subject, &block) - @block = block if block + @block = @block || block @subject = subject @expectation = expect expected_messages_received? diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index a8938471c..65601b5ec 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -88,6 +88,18 @@ module Mocks }.to raise_error(RSpec::Expectations::ExpectationNotMetError) end + it 'gives precedence to a `{ ... }` block when both forms are provided ' + + 'since that form actually binds to `receive`' do + dbl = double(:foo => nil) + called = [] + dbl.foo + expect(dbl).to have_received(:foo) { called << :curly } do + called << :do_end + end + expect(called).to include(:curly) + expect(called).not_to include(:do_end) + end + it 'resets expectations on class methods when mocks are reset' do dbl = Object dbl.stub(:expected_method) From 6ce5d129c8ecc01f1cb7dadefbf0eb40709af6fe Mon Sep 17 00:00:00 2001 From: Tim Cowlishaw Date: Thu, 18 Jul 2013 09:37:08 +0100 Subject: [PATCH 0120/1179] foo ||= bar is nicer syntax than foo = foo || bar --- lib/rspec/mocks/matchers/have_received.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 7ffaedd27..63ae25838 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -14,7 +14,7 @@ def initialize(method_name, &block) end def matches?(subject, &block) - @block = @block || block + @block ||= block @subject = subject @expectation = expect expected_messages_received? From 7be650547cc43eabe514f9d115f60ebfb7022353 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 14 Jul 2013 20:24:15 +0100 Subject: [PATCH 0121/1179] Refine the any_instance passing implementation as per myron's suggestions. Signed-off-by: Sam Phippen --- .../mocks/any_instance/expectation_chain.rb | 2 +- lib/rspec/mocks/any_instance/stub_chain.rb | 2 +- lib/rspec/mocks/configuration.rb | 8 +-- lib/rspec/mocks/message_expectation.rb | 15 +++-- lib/rspec/mocks/proxy.rb | 7 +-- spec/rspec/mocks/any_instance_spec.rb | 63 ++++++++++--------- .../mocks/with_isolated_configuration_spec.rb | 21 +++++++ spec/spec_helper.rb | 12 ++++ 8 files changed, 86 insertions(+), 44 deletions(-) create mode 100644 spec/rspec/mocks/with_isolated_configuration_spec.rb diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 234d3a7a4..97818eed2 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -26,7 +26,7 @@ def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.proxy_for(instance) expected_from = IGNORED_BACKTRACE_LINE me = proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block) - if RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + if RSpec::Mocks.configuration.yield_instance_from_any_instance_implementation_blocks me.and_yield_receiver_to_implementation end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index 75be639d9..ef25cfcb2 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -16,7 +16,7 @@ def create_message_expectation_on(instance) expected_from = IGNORED_BACKTRACE_LINE stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block) - if RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs + if RSpec::Mocks.configuration.yield_instance_from_any_instance_implementation_blocks stub.and_yield_receiver_to_implementation end diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 23a26b3b1..0ba9b820b 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -23,12 +23,12 @@ def add_stub_and_should_receive_to(*modules) end end - def pass_instance_to_any_instance_stubs - @pass_instance_to_any_instance_stubs ||= false + def yield_instance_from_any_instance_implementation_blocks + @yield_instance_from_any_instance_implementation_blocks ||= false end - def pass_instance_to_any_instance_stubs=(arg) - @pass_instance_to_any_instance_stubs = arg + def yield_instance_from_any_instance_implementation_blocks=(arg) + @yield_instance_from_any_instance_implementation_blocks = arg end def syntax=(values) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index c3b80ac97..2a1f8089b 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -6,7 +6,6 @@ class MessageExpectation attr_accessor :error_generator, :implementation attr_reader :message attr_reader :orig_object - attr_reader :yield_receiver_to_implementation attr_writer :expected_received_count, :expected_from, :argument_list_matcher protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation= @@ -27,8 +26,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @args_to_yield = [] @failed_fast = nil @eval_context = nil - @yield_receiver_to_implementation = false - @is_any_instance_expectation = opts[:is_any_instance_expectation] + @yield_receiver_to_implementation_block = false @implementation = Implementation.new self.inner_implementation_action = implementation_block @@ -95,10 +93,14 @@ def and_return(*values, &implementation) end def and_yield_receiver_to_implementation - @yield_receiver_to_implementation = true + @yield_receiver_to_implementation_block = true self end + def yield_receiver_to_implementation_block? + @yield_receiver_to_implementation_block + end + # Tells the object to delegate to the original unmodified method # when it receives the message. # @@ -115,6 +117,7 @@ def and_call_original @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original) else @implementation = AndCallOriginalImplementation.new(@method_double.original_method) + @yield_receiver_to_implementation_block = false end end @@ -183,6 +186,10 @@ def matches?(message, *args) # @private def invoke(parent_stub, *args, &block) + if yield_receiver_to_implementation_block? + args.unshift(orig_object) + end + if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count)) @actual_received_count += 1 @failed_fast = true diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index da5660635..6d8570a25 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -79,6 +79,7 @@ def replay_received_message_on(expectation) expectation.invoke(nil) end end + end # @private @@ -139,14 +140,8 @@ def message_received(message, *args, &block) if expectation = find_almost_matching_expectation(message, *args) expectation.advise(*args) unless expectation.expected_messages_received? end - if stub.yield_receiver_to_implementation - args.unshift(stub.orig_object) - end stub.invoke(nil, *args, &block) elsif expectation - if expectation.yield_receiver_to_implementation - args.unshift(expectation.orig_object) - end expectation.invoke(stub, *args, &block) elsif expectation = find_almost_matching_expectation(message, *args) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index ec9ed3371..2ff18ca5f 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -830,63 +830,70 @@ def foo; end end end - context "passing self" do + context "passing the receiver to the implementation block" do context "when configured to pass the instance" do + include_context 'with isolated configuration' before(:each) do - @orig_pass = RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs - RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs = true - end - - after(:each) do - RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs = @orig_pass + RSpec::Mocks.configuration.yield_instance_from_any_instance_implementation_blocks = true end describe "an any instance stub" do - it "receives the instance" do - klass = Struct.new(:science) + it "passes the instance as the first arg of the implementation block" do instance = klass.new - klass.any_instance.stub(:bees) { |*args| expect(args.first).to eq(instance) } - instance.bees + + expect { |b| + klass.any_instance.should_receive(:bees).with(:sup, &b) + instance.bees(:sup) + }.to yield_with_args(instance, :sup) + end + + it "does not pass the instance to and_call_original" do + klass = Class.new do + def call(*args) + args.first + end + end + klass.any_instance.should_receive(:call).and_call_original + instance = klass.new + expect(instance.call(:bees)).to be :bees end end describe "an any instance expectation" do it "doesn't effect with" do - klass = Struct.new(:science) instance = klass.new klass.any_instance.should_receive(:bees).with(:sup) instance.bees(:sup) end - it "passes the instance" do - klass = Struct.new(:science) + it "passes the instance as the first arg of the implementation block" do instance = klass.new - klass.any_instance.should_receive(:bees).with(:sup) { |*args| expect(args.first).to eq(instance) } - instance.bees(:sup) + + expect { |b| + klass.any_instance.should_receive(:bees).with(:sup, &b) + instance.bees(:sup) + }.to yield_with_args(instance, :sup) end end end context "when configured not to pass the instance" do + include_context 'with isolated configuration' before(:each) do - @orig_pass = RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs - RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs = false - end - - after(:each) do - RSpec::Mocks.configuration.pass_instance_to_any_instance_stubs = @orig_pass + RSpec::Mocks.configuration.yield_instance_from_any_instance_implementation_blocks = false end describe "an any instance stub" do - it "does not receive the instance" do - klass = Struct.new(:science) + it "does not pass the instance to the implementation block" do instance = klass.new - klass.any_instance.stub(:bees) { |*args| expect(args).to be_empty } - instance.bees + + expect { |b| + klass.any_instance.should_receive(:bees).with(:sup, &b) + instance.bees(:sup) + }.to yield_with_args(:sup) end - it "gets data from with correctly" do - klass = Struct.new(:science) + it "does not cause with to fail when the instance is passed" do instance = klass.new klass.any_instance.should_receive(:bees).with(:faces) instance.bees(:faces) diff --git a/spec/rspec/mocks/with_isolated_configuration_spec.rb b/spec/rspec/mocks/with_isolated_configuration_spec.rb new file mode 100644 index 000000000..12d337bde --- /dev/null +++ b/spec/rspec/mocks/with_isolated_configuration_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +module RSpec + module Mocks + describe 'the with isolated configuration shared example group' do + @@c = describe '' do + include_context 'with isolated configuration' + end + it 'resets the configuration' do + @@c.before.first.block.call + RSpec::Mocks.configuration.instance_eval do + def this_method_wont_be_here + end + end + + @@c.after.last.block.call + expect(RSpec::Mocks.configuration.respond_to? :this_method_wont_be_here).to be false + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d160f68a6..3768bb797 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -68,3 +68,15 @@ def reset(object) end end + +shared_context "with isolated configuration" do + orig_configuration = nil + before do + orig_configuration = RSpec::Mocks.configuration + RSpec::Mocks.instance_variable_set(:@configuration, RSpec::Mocks::Configuration.new) + end + + after do + RSpec::Mocks.instance_variable_set(:@configuration, orig_configuration) + end +end From 7feb8a12aa356997d4c7d3775c4b211491ceeaed Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 18 Jul 2013 13:20:06 +0100 Subject: [PATCH 0122/1179] Remove the with isolated configuration spec. Signed-off-by: Sam Phippen --- .../mocks/with_isolated_configuration_spec.rb | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 spec/rspec/mocks/with_isolated_configuration_spec.rb diff --git a/spec/rspec/mocks/with_isolated_configuration_spec.rb b/spec/rspec/mocks/with_isolated_configuration_spec.rb deleted file mode 100644 index 12d337bde..000000000 --- a/spec/rspec/mocks/with_isolated_configuration_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'spec_helper' - -module RSpec - module Mocks - describe 'the with isolated configuration shared example group' do - @@c = describe '' do - include_context 'with isolated configuration' - end - it 'resets the configuration' do - @@c.before.first.block.call - RSpec::Mocks.configuration.instance_eval do - def this_method_wont_be_here - end - end - - @@c.after.last.block.call - expect(RSpec::Mocks.configuration.respond_to? :this_method_wont_be_here).to be false - end - end - end -end From 7ad8f4e8ecf52541a77dfcbdcafc98b01fc22377 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 18 Jul 2013 17:18:50 +0100 Subject: [PATCH 0123/1179] Add a changelog entry for #367. [ci skip] --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 2063f0389..1bfd6bde1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,11 @@ Breaking Changes for 3.0.0: examples. Test doubles are designed to only live for one example. (Myron Marston) +Enhancement: + +* Allow has received matchers to use a block to set further expectations on + arguments. (Tim Cowlishaw) + ### 2.14.1 / 2013-07-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) From 73ae2fabedf6c6fa787e8fa652b78fcec9cdead1 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 18 Jul 2013 19:36:16 +0100 Subject: [PATCH 0124/1179] Improve the changelog entry for #367. [ci skip]. --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1bfd6bde1..fd9a366d0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,8 +17,8 @@ Breaking Changes for 3.0.0: Enhancement: -* Allow has received matchers to use a block to set further expectations on - arguments. (Tim Cowlishaw) +* Allow the `have_received` matcher to use a block to set further expectations + on arguments. (Tim Cowlishaw) ### 2.14.1 / 2013-07-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) From f1487a0d86d446c18cef5946235ac5c09b461ba9 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 Jul 2013 19:55:09 +0200 Subject: [PATCH 0125/1179] Make at_least(0) raise a warning. Signed-off-by: Sam Phippen Conflicts: Changelog.md --- Changelog.md | 1 + lib/rspec/mocks/message_expectation.rb | 7 ++--- spec/rspec/mocks/at_least_spec.rb | 36 +++----------------------- 3 files changed, 9 insertions(+), 35 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2063f0389..87f83ed22 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Breaking Changes for 3.0.0: * Remove support for re-using the same null-object test double in multiple examples. Test doubles are designed to only live for one example. (Myron Marston) +* Make `at_least(0)` raise an error. (Sam Phippen) ### 2.14.1 / 2013-07-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index b6b9a939e..e15d448b7 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -328,12 +328,13 @@ def exactly(n, &block) # # dealer.should_receive(:deal_card).at_least(9).times def at_least(n, &block) + self.inner_implementation_action = block + set_expected_received_count :at_least, n + if n == 0 - RSpec.deprecate "at_least(0) with should_receive", :replacement => "stub" + raise "at_least(0) has been removed, use allow(...).to receive(:message) instead" end - self.inner_implementation_action = block - set_expected_received_count :at_least, n self end diff --git a/spec/rspec/mocks/at_least_spec.rb b/spec/rspec/mocks/at_least_spec.rb index 706aa2c11..ae43d10a5 100644 --- a/spec/rspec/mocks/at_least_spec.rb +++ b/spec/rspec/mocks/at_least_spec.rb @@ -98,38 +98,10 @@ module Mocks end context "when sent with 0" do - before { RSpec.stub(:deprecate) } - - it "outputs a deprecation warning" do - expect(RSpec).to receive(:deprecate).with("at_least\(0\) with should_receive", :replacement => "stub") - expect(@double).to receive(:do_something).at_least(0).times - end - - it "passes with no return if called once" do - @double.should_receive(:do_something).at_least(0).times - @double.do_something - end - - it "passes with return block if called once" do - @double.should_receive(:do_something).at_least(0).times { true } - @double.do_something - end - - it "passes with and_return if called once" do - @double.should_receive(:do_something).at_least(0).times.and_return true - @double.do_something - end - - it "passes with no return if never called" do - @double.should_receive(:do_something).at_least(0).times - end - - it "passes with return block if never called" do - @double.should_receive(:do_something).at_least(0).times { true } - end - - it "passes with and_return if never called" do - @double.should_receive(:do_something).at_least(0).times.and_return true + it "outputs a removal message" do + expect { + expect(@double).to receive(:do_something).at_least(0).times + }.to raise_error(/has been removed/) end end From ebf3a161bd42fb39410955b255b7b3a4a481f5b6 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 18 Jul 2013 13:42:30 +0100 Subject: [PATCH 0126/1179] Unmove the implementation capture as suggested by @jonrowe. Signed-off-by: Sam Phippen --- lib/rspec/mocks/message_expectation.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index e15d448b7..d75d08aa3 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -328,13 +328,14 @@ def exactly(n, &block) # # dealer.should_receive(:deal_card).at_least(9).times def at_least(n, &block) - self.inner_implementation_action = block set_expected_received_count :at_least, n if n == 0 raise "at_least(0) has been removed, use allow(...).to receive(:message) instead" end + self.inner_implementation_action = block + self end From ec969b9b71d4407ef1240e559ede61b44fedf98b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 22 Jul 2013 19:46:19 +1000 Subject: [PATCH 0127/1179] update to truthy falsey --- .../stub_undefined_constant.feature | 6 +-- spec/rspec/mocks/and_yield_spec.rb | 2 +- .../mocks/any_instance/message_chains_spec.rb | 4 +- spec/rspec/mocks/any_instance_spec.rb | 16 +++--- spec/rspec/mocks/argument_expectation_spec.rb | 8 +-- spec/rspec/mocks/bug_report_10263_spec.rb | 2 +- spec/rspec/mocks/bug_report_8165_spec.rb | 4 +- ...bining_implementation_instructions_spec.rb | 6 +-- spec/rspec/mocks/matchers/receive_spec.rb | 4 +- spec/rspec/mocks/mock_spec.rb | 2 +- spec/rspec/mocks/mutate_const_spec.rb | 50 +++++++++---------- spec/rspec/mocks/record_messages_spec.rb | 8 +-- 12 files changed, 56 insertions(+), 56 deletions(-) diff --git a/features/mutating_constants/stub_undefined_constant.feature b/features/mutating_constants/stub_undefined_constant.feature index 0379ec414..48bb509a4 100644 --- a/features/mutating_constants/stub_undefined_constant.feature +++ b/features/mutating_constants/stub_undefined_constant.feature @@ -33,15 +33,15 @@ Feature: Stub Undefined Constant module MyGem describe SomeClass do it "can stub an arbitrarily deep constant that is undefined" do - defined?(SomeClass::A).should be_false + defined?(SomeClass::A).should be_falsey stub_const("MyGem::SomeClass::A::B::C", 3) SomeClass::A::B::C.should eq(3) SomeClass::A.should be_a(Module) end it 'undefines the intermediary constants that were dynamically created' do - defined?(SomeClass).should be_true - defined?(SomeClass::A).should be_false + defined?(SomeClass).should be_truthy + defined?(SomeClass::A).should be_falsey end end end diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index f061a4e4d..dd72346ea 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -25,7 +25,7 @@ obj.stub(:method_that_accepts_a_block).and_yield do |eval_context| evaluated = true end - expect(evaluated).to be_true + expect(evaluated).to be_truthy end it "passes an eval context object to the supplied block" do diff --git a/spec/rspec/mocks/any_instance/message_chains_spec.rb b/spec/rspec/mocks/any_instance/message_chains_spec.rb index 398d4db6d..5df116007 100644 --- a/spec/rspec/mocks/any_instance/message_chains_spec.rb +++ b/spec/rspec/mocks/any_instance/message_chains_spec.rb @@ -7,13 +7,13 @@ it "knows if a method does not have an expectation set on it" do chains.add(:method_name, stub_chain) - expect(chains.has_expectation?(:method_name)).to be_false + expect(chains.has_expectation?(:method_name)).to be_falsey end it "knows if a method has an expectation set on it" do chains.add(:method_name, stub_chain) chains.add(:method_name, expectation_chain) - expect(chains.has_expectation?(:method_name)).to be_true + expect(chains.has_expectation?(:method_name)).to be_truthy end it "can remove all stub chains" do diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 555357b63..93f0d0ee1 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -696,13 +696,13 @@ def foo; end context "public methods" do before(:each) do klass.any_instance.stub(:existing_method).and_return(1) - expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_true + expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_truthy end it "restores the class to its original state after each example when no instance is created" do space.verify_all - expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_false + expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) end @@ -711,7 +711,7 @@ def foo; end space.verify_all - expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_false + expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) end @@ -721,7 +721,7 @@ def foo; end space.verify_all - expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_false + expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) end end @@ -733,11 +733,11 @@ def foo; end end it "cleans up the backed up method" do - expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_false + expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey end it "restores a stubbed private method after the spec is run" do - expect(klass.private_method_defined?(:private_method)).to be_true + expect(klass.private_method_defined?(:private_method)).to be_truthy end it "ensures that the restored method behaves as it originally did" do @@ -755,11 +755,11 @@ def foo; end end it "cleans up the backed up method" do - expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_false + expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey end it "restores a stubbed private method after the spec is run" do - expect(klass.private_method_defined?(:private_method)).to be_true + expect(klass.private_method_defined?(:private_method)).to be_truthy end it "ensures that the restored method behaves as it originally did" do diff --git a/spec/rspec/mocks/argument_expectation_spec.rb b/spec/rspec/mocks/argument_expectation_spec.rb index f1e5ff34d..e0a9a92ad 100644 --- a/spec/rspec/mocks/argument_expectation_spec.rb +++ b/spec/rspec/mocks/argument_expectation_spec.rb @@ -9,23 +9,23 @@ module Mocks it "considers an object that responds to #matches? and #failure_message_for_should to be a matcher" do obj.stub(:matches?) obj.stub(:failure_message_for_should) - expect(argument_expectation.send(:is_matcher?, obj)).to be_true + expect(argument_expectation.send(:is_matcher?, obj)).to be_truthy end it "considers an object that responds to #matches? and #failure_message to be a matcher for backward compatibility" do obj.stub(:matches?) obj.stub(:failure_message) - expect(argument_expectation.send(:is_matcher?, obj)).to be_true + expect(argument_expectation.send(:is_matcher?, obj)).to be_truthy end it "does NOT consider an object that only responds to #matches? to be a matcher" do obj.stub(:matches?) - expect(argument_expectation.send(:is_matcher?, obj)).to be_false + expect(argument_expectation.send(:is_matcher?, obj)).to be_falsey end it "does not consider a null object to be a matcher" do obj.as_null_object - expect(argument_expectation.send(:is_matcher?, obj)).to be_false + expect(argument_expectation.send(:is_matcher?, obj)).to be_falsey end end end diff --git a/spec/rspec/mocks/bug_report_10263_spec.rb b/spec/rspec/mocks/bug_report_10263_spec.rb index 6b58e6bee..5c1bdc580 100644 --- a/spec/rspec/mocks/bug_report_10263_spec.rb +++ b/spec/rspec/mocks/bug_report_10263_spec.rb @@ -5,7 +5,7 @@ specify "when one example has an expectation inside the block passed to should_receive" do test_double.should_receive(:msg) do |arg| - expect(arg).to be_true #this call exposes the problem + expect(arg).to be_truthy #this call exposes the problem end begin test_double.msg(false) diff --git a/spec/rspec/mocks/bug_report_8165_spec.rb b/spec/rspec/mocks/bug_report_8165_spec.rb index bf0de63d4..e355789b3 100644 --- a/spec/rspec/mocks/bug_report_8165_spec.rb +++ b/spec/rspec/mocks/bug_report_8165_spec.rb @@ -16,7 +16,7 @@ obj = Object.new obj.should_receive(:respond_to?).with(:foobar).and_return(true) obj.should_receive(:foobar).and_return(:baz) - expect(obj.respond_to?(:foobar)).to be_true + expect(obj.respond_to?(:foobar)).to be_truthy expect(obj.foobar).to eq :baz end @@ -24,7 +24,7 @@ obj = double("obj") obj.should_receive(:respond_to?).with(:foobar).and_return(true) obj.should_receive(:foobar).and_return(:baz) - expect(obj.respond_to?(:foobar)).to be_true + expect(obj.respond_to?(:foobar)).to be_truthy expect(obj.foobar).to eq :baz end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index f00132b1f..7904268b9 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -21,7 +21,7 @@ def verify_combined_implementation expect(dbl.foo(:arg, &b)).to eq(3) }.to yield_with_args(5) - expect(@block_called).to be_true + expect(@block_called).to be_truthy end it 'works when passing a block to `stub`' do @@ -111,7 +111,7 @@ def verify_combined_implementation expect { dbl.foo(&b) }.to raise_error("boom") }.to yield_with_args(5) - expect(block_called).to be_true + expect(block_called).to be_truthy end it 'can combine and_yield and and_throw' do @@ -132,7 +132,7 @@ def verify_combined_implementation expect { dbl.foo(&b) }.to throw_symbol(:bar) }.to yield_with_args(5) - expect(block_called).to be_true + expect(block_called).to be_truthy end it 'returns `nil` from all terminal actions to discourage further configuration' do diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 8012ce938..bab175031 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -282,7 +282,7 @@ def eq(value) context "when rspec-expectations is included in the test framework first" do before do # the examples here assume `expect` is define in RSpec::Matchers... - expect(RSpec::Matchers.method_defined?(:expect)).to be_true + expect(RSpec::Matchers.method_defined?(:expect)).to be_truthy end let(:framework) do @@ -304,7 +304,7 @@ def eq(value) context "when rspec-expectations is included in the test framework last" do before do # the examples here assume `expect` is define in RSpec::Matchers... - expect(RSpec::Matchers.method_defined?(:expect)).to be_true + expect(RSpec::Matchers.method_defined?(:expect)).to be_truthy end let(:framework) do diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 1a504ba46..d20c031d8 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -205,7 +205,7 @@ def @double.method_with_default_argument(arg={}); end it "fails if expectation block fails" do @double.should_receive(:something) do |bool| - expect(bool).to be_true + expect(bool).to be_truthy end expect { diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index b2abe8371..9f252b4a9 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -78,18 +78,18 @@ def change_const_value_to(value) shared_examples_for "loaded constant hiding" do |const_name| before do - expect(recursive_const_defined?(const_name)).to be_true + expect(recursive_const_defined?(const_name)).to be_truthy end it 'allows it to be hidden' do hide_const(const_name) - expect(recursive_const_defined?(const_name)).to be_false + expect(recursive_const_defined?(const_name)).to be_falsey end it 'resets the constant when rspec clear its mocks' do hide_const(const_name) reset_rspec_mocks - expect(recursive_const_defined?(const_name)).to be_true + expect(recursive_const_defined?(const_name)).to be_truthy end it 'returns nil' do @@ -101,7 +101,7 @@ def change_const_value_to(value) include_context "constant example methods", const_name before do - expect(recursive_const_defined?(const_name)).to be_false + expect(recursive_const_defined?(const_name)).to be_falsey end it 'allows it to be stubbed' do @@ -112,7 +112,7 @@ def change_const_value_to(value) it 'removes the constant when rspec clears its mocks' do stub_const(const_name, 7) reset_rspec_mocks - expect(recursive_const_defined?(const_name)).to be_false + expect(recursive_const_defined?(const_name)).to be_falsey end it 'returns the stubbed value' do @@ -130,18 +130,18 @@ def change_const_value_to(value) include_context "constant example methods", const_name before do - expect(recursive_const_defined?(const_name)).to be_false + expect(recursive_const_defined?(const_name)).to be_falsey end it 'allows it to be hidden, though the operation has no effect' do hide_const(const_name) - expect(recursive_const_defined?(const_name)).to be_false + expect(recursive_const_defined?(const_name)).to be_falsey end it 'remains undefined after rspec clears its mocks' do hide_const(const_name) reset_rspec_mocks - expect(recursive_const_defined?(const_name)).to be_false + expect(recursive_const_defined?(const_name)).to be_falsey end it 'returns nil' do @@ -206,7 +206,7 @@ def change_const_value_to(value) orig_value = TOP_LEVEL_VALUE_CONST hide_const("TOP_LEVEL_VALUE_CONST") - expect(recursive_const_defined?("TOP_LEVEL_VALUE_CONST")).to be_false + expect(recursive_const_defined?("TOP_LEVEL_VALUE_CONST")).to be_falsey stub_const("TOP_LEVEL_VALUE_CONST", 12345) expect(TOP_LEVEL_VALUE_CONST).to eq 12345 @@ -247,8 +247,8 @@ def change_const_value_to(value) stub = Module.new stub_const("TestSubClass", stub, :transfer_nested_constants => true) expect(stub::P).to eq(:p) - expect(defined?(stub::M)).to be_false - expect(defined?(stub::N)).to be_false + expect(defined?(stub::M)).to be_falsey + expect(defined?(stub::N)).to be_falsey end it 'raises an error when asked to transfer a nested inherited constant' do @@ -266,7 +266,7 @@ def change_const_value_to(value) stub_const("TestClass", stub, :transfer_nested_constants => [:M, :N]) expect(stub::M).to eq(:m) expect(stub::N).to eq(:n) - expect(defined?(stub::Nested)).to be_false + expect(defined?(stub::Nested)).to be_falsey end it 'raises an error if asked to transfer nested constants but given an object that does not support them' do @@ -302,7 +302,7 @@ def change_const_value_to(value) it 'raises an error if asked to transfer a nested constant that is not defined' do original_tc = TestClass - expect(defined?(TestClass::V)).to be_false + expect(defined?(TestClass::V)).to be_falsey stub = Module.new expect { @@ -345,10 +345,10 @@ def change_const_value_to(value) it_behaves_like "unloaded constant stubbing", "X::Y" it 'removes the root constant when rspec clears its mocks' do - expect(defined?(X)).to be_false + expect(defined?(X)).to be_falsey stub_const("X::Y", 7) reset_rspec_mocks - expect(defined?(X)).to be_false + expect(defined?(X)).to be_falsey end end @@ -356,10 +356,10 @@ def change_const_value_to(value) it_behaves_like "unloaded constant stubbing", "X::Y::Z" it 'removes the root constant when rspec clears its mocks' do - expect(defined?(X)).to be_false + expect(defined?(X)).to be_falsey stub_const("X::Y::Z", 7) reset_rspec_mocks - expect(defined?(X)).to be_false + expect(defined?(X)).to be_falsey end end @@ -367,12 +367,12 @@ def change_const_value_to(value) it_behaves_like "unloaded constant stubbing", "TestClass::X" it 'removes the unloaded constant but leaves the loaded constant when rspec resets its mocks' do - expect(defined?(TestClass)).to be_true - expect(defined?(TestClass::X)).to be_false + expect(defined?(TestClass)).to be_truthy + expect(defined?(TestClass::X)).to be_falsey stub_const("TestClass::X", 7) reset_rspec_mocks - expect(defined?(TestClass)).to be_true - expect(defined?(TestClass::X)).to be_false + expect(defined?(TestClass)).to be_truthy + expect(defined?(TestClass::X)).to be_falsey end it 'raises a helpful error if it cannot be stubbed due to an intermediary constant that is not a module' do @@ -385,12 +385,12 @@ def change_const_value_to(value) it_behaves_like "unloaded constant stubbing", "TestClass::Nested::NestedEvenMore::X::Y::Z" it 'removes the first unloaded constant but leaves the loaded nested constant when rspec resets its mocks' do - expect(defined?(TestClass::Nested::NestedEvenMore)).to be_true - expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_false + expect(defined?(TestClass::Nested::NestedEvenMore)).to be_truthy + expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_falsey stub_const("TestClass::Nested::NestedEvenMore::X::Y::Z", 7) reset_rspec_mocks - expect(defined?(TestClass::Nested::NestedEvenMore)).to be_true - expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_false + expect(defined?(TestClass::Nested::NestedEvenMore)).to be_truthy + expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_falsey end end end diff --git a/spec/rspec/mocks/record_messages_spec.rb b/spec/rspec/mocks/record_messages_spec.rb index a61ff0739..6ba1273c6 100644 --- a/spec/rspec/mocks/record_messages_spec.rb +++ b/spec/rspec/mocks/record_messages_spec.rb @@ -7,19 +7,19 @@ module Mocks @mock = double("mock").as_null_object end it "answers false for received_message? when no messages received" do - expect(@mock.received_message?(:message)).to be_false + expect(@mock.received_message?(:message)).to be_falsey end it "answers true for received_message? when message received" do @mock.message - expect(@mock.received_message?(:message)).to be_true + expect(@mock.received_message?(:message)).to be_truthy end it "answers true for received_message? when message received with correct args" do @mock.message 1,2,3 - expect(@mock.received_message?(:message, 1,2,3)).to be_true + expect(@mock.received_message?(:message, 1,2,3)).to be_truthy end it "answers false for received_message? when message received with incorrect args" do @mock.message 1,2,3 - expect(@mock.received_message?(:message, 1,2)).to be_false + expect(@mock.received_message?(:message, 1,2)).to be_falsey end end end From c4d2f80655e9e618dfcf8459740f3bde17494eab Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 22 Jul 2013 20:29:40 -0700 Subject: [PATCH 0128/1179] Remove deprecated file. --- Changelog.md | 2 ++ lib/spec/mocks.rb | 4 ---- spec/rspec/mocks_spec.rb | 7 ------- 3 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 lib/spec/mocks.rb diff --git a/Changelog.md b/Changelog.md index 11291c7ae..30dc1c99d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,8 @@ Breaking Changes for 3.0.0: examples. Test doubles are designed to only live for one example. (Myron Marston) * Make `at_least(0)` raise an error. (Sam Phippen) +* Remove support for `require 'spec/mocks'` which had been kept + in place for backwards compatibility with rspec 1 (Myron Marston). Enhancement: diff --git a/lib/spec/mocks.rb b/lib/spec/mocks.rb deleted file mode 100644 index a6ca01e75..000000000 --- a/lib/spec/mocks.rb +++ /dev/null @@ -1,4 +0,0 @@ -# For backward compatibility with rspec-1 -require 'rspec/mocks' - -RSpec.deprecate "require 'spec/mocks'", :replacement => "require 'rspec/mocks'" diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 1b2f1debe..d8e30a808 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -55,11 +55,4 @@ expect(RSpec::Mocks.configuration).to be(RSpec::Mocks.configuration) end end - - context 'when requiring spec/mocks (as was valid for rspec 1)' do - it 'prints a deprecation warning' do - expect(::RSpec).to receive(:deprecate).with("require 'spec/mocks'", :replacement => "require 'rspec/mocks'") - load "spec/mocks.rb" - end - end end From 839cf335e781466d6ba1e334f834394b8c964df6 Mon Sep 17 00:00:00 2001 From: maxlinc Date: Wed, 24 Jul 2013 15:36:33 -0300 Subject: [PATCH 0129/1179] Add link to Test Double bliki I noticed you are already linking to the xUnitPatterns definitions and Martin Fowler's bliki Mocks Aren't Stubs. I thought it would be good to link to Martin Fowler's Test Double bliki. I feel it is a clear, compact summary of the information in the other two links. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1407229c0..cb0ba10a2 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,7 @@ you are interested in learning more, here is some recommended reading: * Mock Objects: http://www.mockobjects.com/ * Endo-Testing: http://stalatest.googlecode.com/svn/trunk/Literatur/mockobjects.pdf * Mock Roles, Not Objects: http://jmock.org/oopsla2004.pdf +* Test Double: http://www.martinfowler.com/bliki/TestDouble.html * Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html * Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html From 7293bfa7c5a70dc231f4d678f459c9a34f750114 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 25 Jul 2013 14:31:45 +0100 Subject: [PATCH 0130/1179] Turn on yielding to any_instance by default and fix config handling. Signed-off-by: Sam Phippen --- lib/rspec/mocks/configuration.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 0ba9b820b..4747fcfe3 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -2,6 +2,13 @@ module RSpec module Mocks # Provides configuration options for rspec-mocks. class Configuration + + def initialize + @yield_instance_from_any_instance_implementation_blocks = true + end + + attr_accessor :yield_instance_from_any_instance_implementation_blocks + # Adds `stub` and `should_receive` to the given # modules or classes. This is usually only necessary # if you application uses some proxy classes that @@ -23,14 +30,6 @@ def add_stub_and_should_receive_to(*modules) end end - def yield_instance_from_any_instance_implementation_blocks - @yield_instance_from_any_instance_implementation_blocks ||= false - end - - def yield_instance_from_any_instance_implementation_blocks=(arg) - @yield_instance_from_any_instance_implementation_blocks = arg - end - def syntax=(values) if Array(values).include?(:expect) Syntax.enable_expect From c104d49696301589fad0a0202f8a387258dc54ed Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 25 Jul 2013 23:41:36 +0100 Subject: [PATCH 0131/1179] Use any instance yield names myron suggested. Signed-off-by: Sam Phippen --- lib/rspec/mocks/any_instance/expectation_chain.rb | 2 +- lib/rspec/mocks/any_instance/stub_chain.rb | 2 +- lib/rspec/mocks/configuration.rb | 10 ++++++++-- spec/rspec/mocks/any_instance_spec.rb | 4 ++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 97818eed2..6646f6675 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -26,7 +26,7 @@ def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.proxy_for(instance) expected_from = IGNORED_BACKTRACE_LINE me = proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block) - if RSpec::Mocks.configuration.yield_instance_from_any_instance_implementation_blocks + if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? me.and_yield_receiver_to_implementation end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index ef25cfcb2..735058ed9 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -16,7 +16,7 @@ def create_message_expectation_on(instance) expected_from = IGNORED_BACKTRACE_LINE stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block) - if RSpec::Mocks.configuration.yield_instance_from_any_instance_implementation_blocks + if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? stub.and_yield_receiver_to_implementation end diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 4747fcfe3..bd9626051 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -4,10 +4,16 @@ module Mocks class Configuration def initialize - @yield_instance_from_any_instance_implementation_blocks = true + @yield_receiver_to_any_instance_implementation_blocks = true end - attr_accessor :yield_instance_from_any_instance_implementation_blocks + def yield_receiver_to_any_instance_implementation_blocks? + @yield_receiver_to_any_instance_implementation_blocks + end + + def yield_receiver_to_any_instance_implementation_blocks=(arg) + @yield_receiver_to_any_instance_implementation_blocks = arg + end # Adds `stub` and `should_receive` to the given # modules or classes. This is usually only necessary diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 2ff18ca5f..5dea05da6 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -834,7 +834,7 @@ def foo; end context "when configured to pass the instance" do include_context 'with isolated configuration' before(:each) do - RSpec::Mocks.configuration.yield_instance_from_any_instance_implementation_blocks = true + RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = true end describe "an any instance stub" do @@ -880,7 +880,7 @@ def call(*args) context "when configured not to pass the instance" do include_context 'with isolated configuration' before(:each) do - RSpec::Mocks.configuration.yield_instance_from_any_instance_implementation_blocks = false + RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = false end describe "an any instance stub" do From a4f908f439ac255f31e60c657b521123d5f958b7 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 25 Jul 2013 23:43:11 +0100 Subject: [PATCH 0132/1179] Add a changelog entry for #351. Signed-off-by: Sam Phippen --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 2063f0389..e59db4bef 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,10 @@ Breaking Changes for 3.0.0: examples. Test doubles are designed to only live for one example. (Myron Marston) +Enhancements: + +* Yield the receiver to `any_instance` implementation blocks (Sam Phippen). + ### 2.14.1 / 2013-07-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) From 35cc00695591c0dc4f3a85b55048e805f272c520 Mon Sep 17 00:00:00 2001 From: Michi Huber Date: Sat, 9 Mar 2013 10:02:19 +0100 Subject: [PATCH 0133/1179] remove #stub and #mock --- Changelog.md | 1 + lib/rspec/mocks/error_generator.rb | 7 +++-- lib/rspec/mocks/example_methods.rb | 10 ++----- lib/rspec/mocks/proxy.rb | 2 +- lib/rspec/mocks/test_double.rb | 39 +++++++--------------------- spec/rspec/mocks/test_double_spec.rb | 19 +------------- 6 files changed, 17 insertions(+), 61 deletions(-) diff --git a/Changelog.md b/Changelog.md index 30dc1c99d..8322a2500 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Breaking Changes for 3.0.0: +* Removed `double`-aliases `stub` and `mock`. (Michi Huber) * Raise an explicit error if `should_not_receive(...).and_return` is used. (Sam Phippen) * Remove 1.8.6 workarounds (Jon Rowe) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 6e39238a7..b51a8e656 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -4,8 +4,7 @@ module Mocks class ErrorGenerator attr_writer :opts - def initialize(target, name, options={}) - @declared_as = options[:__declared_as] || 'Mock' + def initialize(target, name) @target = target @name = name end @@ -127,9 +126,9 @@ def self.raise_double_negation_error(wrapped_expression) def intro if @name - "#{@declared_as} #{@name.inspect}" + "Double #{@name.inspect}" elsif TestDouble === @target - @declared_as + "Double" elsif Class === @target "<#{@target.inspect} (class)>" elsif @target diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index eec6205ab..b7702a4b6 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -26,7 +26,8 @@ module ExampleMethods # card.rank #=> "A" # def double(*args) - declare_double('Double', *args) + args << {} unless Hash === args.last + RSpec::Mocks::Mock.new(*args) end # Disables warning messages about expectations being set on nil. @@ -126,13 +127,6 @@ def self.included(klass) end private - - def declare_double(declared_as, *args) - args << {} unless Hash === args.last - args.last[:__declared_as] = declared_as - RSpec::Mocks::Mock.new(*args) - end - # This module exists to host the `expect` method for cases where # rspec-mocks is used w/o rspec-expectations. module ExpectHost diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 68046bd09..2076cce6b 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -7,7 +7,7 @@ class Proxy def initialize(object, name=nil, options={}) @object = object @name = name - @error_generator = ErrorGenerator.new object, name, options + @error_generator = ErrorGenerator.new(object, name) @expectation_ordering = RSpec::Mocks::space.expectation_ordering @messages_received = [] @options = options diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index b27e26c56..2c01060ef 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -12,15 +12,15 @@ module TestDouble # module = Module.new # RSpec::Mocks::TestDouble.extend_onto(module, "MyMixin", :foo => "bar") # module.foo #=> "bar" - def self.extend_onto(object, name=nil, stubs_and_options={}) + def self.extend_onto(object, name=nil, stubs={}) object.extend self - object.send(:__initialize_as_test_double, name, stubs_and_options) + object.send(:__initialize_as_test_double, name, stubs) end # Creates a new test double with a `name` (that will be used in error # messages only) - def initialize(name=nil, stubs_and_options={}) - __initialize_as_test_double(name, stubs_and_options) + def initialize(name=nil, stubs={}) + __initialize_as_test_double(name, stubs) end # Tells the object to respond to all messages. If specific stub values @@ -62,20 +62,19 @@ def respond_to?(message, incl_private=false) # @private def __build_mock_proxy - Proxy.new(self, @name, @options || {}) + Proxy.new(self, @name) end private - def __initialize_as_test_double(name=nil, stubs_and_options={}) - if name.is_a?(Hash) && stubs_and_options.empty? - stubs_and_options = name + def __initialize_as_test_double(name=nil, stubs={}) + if name.is_a?(Hash) && stubs.empty? + stubs = name @name = nil else @name = name end - @options = extract_options(stubs_and_options) - assign_stubs(stubs_and_options) + assign_stubs(stubs) end def method_missing(message, *args, &block) @@ -96,32 +95,12 @@ def method_missing(message, *args, &block) end end - def extract_options(stubs_and_options) - if stubs_and_options[:null_object] - @null_object = stubs_and_options.delete(:null_object) - RSpec.deprecate("double('name', :null_object => true)", :replacement => "double('name').as_null_object") - end - options = {} - extract_option(stubs_and_options, options, :__declared_as, 'Mock') - options - end - - def extract_option(source, target, key, default=nil) - if source[key] - target[key] = source.delete(key) - elsif default - target[key] = default - end - end - def assign_stubs(stubs) stubs.each_pair do |message, response| Mocks.allow_message(self, message).and_return(response) end end - private - def __mock_proxy ::RSpec::Mocks.proxy_for(self) end diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index c396c5c9f..45e1c75e6 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -28,7 +28,7 @@ def use; end it 'sets the test double name when a name is passed' do double = Module.new { TestDouble.extend_onto(self, "MyDouble") } - expect { double.foo }.to raise_error(/Mock "MyDouble" received/) + expect { double.foo }.to raise_error(/Double "MyDouble" received/) end [[:should, :expect], [:expect], [:should]].each do |syntax| @@ -44,23 +44,6 @@ def use; end end end end - - it 'indicates what type of test double it is in error messages' do - double = Module.new do - TestDouble.extend_onto(self, "A", :__declared_as => "ModuleMock") - end - expect { double.foo }.to raise_error(/ModuleMock "A"/) - end - - it 'is declared as a mock by default' do - double = Module.new { TestDouble.extend_onto(self) } - expect { double.foo }.to raise_error(/Mock received/) - end - - it 'warns of deprecation of :null_object => true' do - expect(RSpec).to receive(:deprecate) - Class.new { TestDouble.extend_onto self, 'name', :null_object => true } - end end end end From 38377efc9125bed90bea19c804bbac47e045cf0c Mon Sep 17 00:00:00 2001 From: Michi Huber Date: Sat, 9 Mar 2013 13:30:43 +0100 Subject: [PATCH 0134/1179] Don't override let definitions with argument matchers see https://github.com/rspec/rspec-mocks/issues/102 ArgumentMatchers (together with ExampleMethods) were included into the ExampleGroup instance's metaclass. This caused argument matcher methods such as boolean() or hash_containing() to override let definitions with the same name. This commit fixes this bug by including ArgumentMatchers into the example group's class and not into the metaclass. --- Changelog.md | 5 +++++ lib/rspec/mocks.rb | 4 +--- spec/rspec/mocks/mock_spec.rb | 13 +++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8322a2500..35e9c4222 100644 --- a/Changelog.md +++ b/Changelog.md @@ -24,6 +24,11 @@ Enhancement: * Allow the `have_received` matcher to use a block to set further expectations on arguments. (Tim Cowlishaw) +Bug Fixes: + +* ArgumentMatcher methods (e.g. `hash_containing`) no longer overwrite + `let`-definitions of the same name. (Michi Huber) + ### 2.14.1 / 2013-07-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 2591bbe75..843b60c1e 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -8,9 +8,7 @@ class << self attr_accessor :space def setup(host) - (class << host; self; end).class_exec do - include RSpec::Mocks::ExampleMethods - end + host.class.class_exec { include RSpec::Mocks::ExampleMethods } self.space ||= RSpec::Mocks::Space.new end diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index d20c031d8..8e2c1aafc 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -662,6 +662,19 @@ def add_call end end + describe "ArgumentMatchers not overriding let definitions" do + WithMatchers = Class.new { include RSpec::Mocks::ArgumentMatchers } + methods = WithMatchers.new.methods - Object.new.methods + + methods.each do |method| + let(method) { :a_thing } + + it "doesn't override a let named #{ method }" do + expect(send(method)).to be(:a_thing) + end + end + end + describe 'string representation generated by #to_s' do it 'does not contain < because that might lead to invalid HTML in some situations' do double = double("Dog") From bd2cd7d910e0d5e91d64f2388bf15d337d5ece76 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 26 Jul 2013 21:12:21 +1000 Subject: [PATCH 0135/1179] Revert "Don't override let definitions with argument matchers" This reverts commit 38377efc9125bed90bea19c804bbac47e045cf0c. --- Changelog.md | 5 ----- lib/rspec/mocks.rb | 4 +++- spec/rspec/mocks/mock_spec.rb | 13 ------------- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/Changelog.md b/Changelog.md index 35e9c4222..8322a2500 100644 --- a/Changelog.md +++ b/Changelog.md @@ -24,11 +24,6 @@ Enhancement: * Allow the `have_received` matcher to use a block to set further expectations on arguments. (Tim Cowlishaw) -Bug Fixes: - -* ArgumentMatcher methods (e.g. `hash_containing`) no longer overwrite - `let`-definitions of the same name. (Michi Huber) - ### 2.14.1 / 2013-07-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 843b60c1e..2591bbe75 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -8,7 +8,9 @@ class << self attr_accessor :space def setup(host) - host.class.class_exec { include RSpec::Mocks::ExampleMethods } + (class << host; self; end).class_exec do + include RSpec::Mocks::ExampleMethods + end self.space ||= RSpec::Mocks::Space.new end diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index 8e2c1aafc..d20c031d8 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -662,19 +662,6 @@ def add_call end end - describe "ArgumentMatchers not overriding let definitions" do - WithMatchers = Class.new { include RSpec::Mocks::ArgumentMatchers } - methods = WithMatchers.new.methods - Object.new.methods - - methods.each do |method| - let(method) { :a_thing } - - it "doesn't override a let named #{ method }" do - expect(send(method)).to be(:a_thing) - end - end - end - describe 'string representation generated by #to_s' do it 'does not contain < because that might lead to invalid HTML in some situations' do double = double("Dog") From 92026151ed9af3741afff708dde467ff468af01b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 28 Jul 2013 18:45:53 +1000 Subject: [PATCH 0136/1179] rspec-mocks is always loaded from source via `gemspec` command --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 5c32c173f..2345f9dda 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" gemspec -%w[rspec rspec-core rspec-expectations rspec-mocks].each do |lib| +%w[rspec rspec-core rspec-expectations].each do |lib| library_path = File.expand_path("../../#{lib}", __FILE__) if File.exist?(library_path) gem lib, :path => library_path From d627d3d93d544a7787bc7ab856a82cb9b1d9335f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Jul 2013 09:54:24 -0700 Subject: [PATCH 0137/1179] Update changelog to reflect 2.14.2 release. [ci skip] --- Changelog.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index e29446afa..702c5e91c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### Development -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...master) +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...master) Breaking Changes for 3.0.0: @@ -28,6 +28,16 @@ Enhancements: * Yield the receiver to `any_instance` implementation blocks (Sam Phippen). +### 2.14.2 / 2013-07-30 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.1...v2.14.2) + +Bug Fixes: + +* Fix `as_null_object` doubles so that they return `nil` from `to_ary` + (Jon Rowe). +* Fix regression in 2.14 that made `stub!` (with an implicit receiver) + return a test double rather than stub a method (Myron Marston). + ### 2.14.1 / 2013-07-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) From 414563efa6a253957ad11af5a7c0d3ab00c82088 Mon Sep 17 00:00:00 2001 From: Bradley Schaefer Date: Mon, 29 Jul 2013 10:47:49 -0500 Subject: [PATCH 0138/1179] Exhibit problem with stubbing and prepend As described in #384 the way that original methods are stashed breaks the ability to stub the non-prepended method. --- spec/rspec/mocks/stub_spec.rb | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 1acad9481..adb9d72f2 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -138,6 +138,43 @@ class << self; public :hello; end; expect(mod.hello).to eq(:hello) end + + if RUBY_VERSION >= '2.0.0' + context "with a prepended module (ruby 2.0.0+)" do + before do + mod = Module.new do + def existing_instance_method + "#{super}_prepended".to_sym + end + end + + @prepended_class = Class.new(@class) do + prepend mod + + def non_prepended_method + :not_prepended + end + end + @prepended_instance = @prepended_class.new + end + + it "restores prepended instance methods" do + allow(@prepended_instance).to receive(:existing_instance_method) { :stubbed } + expect(@prepended_instance.existing_instance_method).to eq :stubbed + + reset @prepended_instance + expect(@prepended_instance.existing_instance_method).to eq :original_value_prepended + end + + it "restores non-prepended instance methods" do + allow(@prepended_instance).to receive(:non_prepended_method) { :stubbed } + expect(@prepended_instance.non_prepended_method).to eq :stubbed + + reset @prepended_instance + expect(@prepended_instance.non_prepended_method).to eq :not_prepended + end + end + end end it "returns values in order to consecutive calls" do From 3956221a127d560555e0bdbb1347f330c9237cd8 Mon Sep 17 00:00:00 2001 From: Bradley Schaefer Date: Mon, 29 Jul 2013 17:24:49 -0500 Subject: [PATCH 0139/1179] Allow stubbing instance methods on a prepended class --- lib/rspec/mocks/instance_method_stasher.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index 53609428d..d3c220755 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -36,6 +36,12 @@ def method_defined_on_klass?(klass = @klass) def method_owned_by_klass? owner = @klass.instance_method(@method).owner + + # On Ruby 2.0.0+ the owner of a method on a class which has been + # `prepend`ed may actually be an instance, e.g. + # `#`, rather than the expected `MyClass`. + owner = owner.class unless owner.is_a? Class + # On some 1.9s (e.g. rubinius) aliased methods # can report the wrong owner. Example: # class MyClass From 9636dc9a9d681a66d9d094522f3e9e8972029d7a Mon Sep 17 00:00:00 2001 From: Bradley Schaefer Date: Mon, 29 Jul 2013 17:35:14 -0500 Subject: [PATCH 0140/1179] Changelog entry --- Changelog.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index e29446afa..b0d71f7c3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,14 +19,16 @@ Breaking Changes for 3.0.0: * Remove support for `require 'spec/mocks'` which had been kept in place for backwards compatibility with rspec 1 (Myron Marston). -Enhancement: +Enhancements: * Allow the `have_received` matcher to use a block to set further expectations on arguments. (Tim Cowlishaw) +* Yield the receiver to `any_instance` implementation blocks (Sam Phippen). -Enhancements: +Bug Fixes: -* Yield the receiver to `any_instance` implementation blocks (Sam Phippen). +* Fix stubbing some instance methods for classes whose hierarchy includes + a prepended Module (Bradley Schaefer) ### 2.14.1 / 2013-07-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) From 03f51d9366b7c4f019168ea6021a6646a0496cfb Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 20 Jul 2013 08:33:12 -0700 Subject: [PATCH 0141/1179] Add verifying double features from rspec-fire. This is intended to be both API compatible with rspec-fire, and to completely obsolete it. Fixes #227. --- Changelog.md | 2 + features/.nav | 4 + .../verifying_doubles/class_doubles.feature | 88 +++++++ .../verifying_doubles/dynamic_classes.feature | 69 ++++++ .../verifying_doubles/introduction.feature | 85 +++++++ lib/rspec/mocks/arity_calculator.rb | 71 ++++++ lib/rspec/mocks/configuration.rb | 14 ++ lib/rspec/mocks/error_generator.rb | 16 ++ lib/rspec/mocks/example_methods.rb | 58 ++++- lib/rspec/mocks/framework.rb | 1 + lib/rspec/mocks/method_double.rb | 14 +- lib/rspec/mocks/module_reference.rb | 37 +++ lib/rspec/mocks/proxy.rb | 4 +- lib/rspec/mocks/verifying_double.rb | 57 +++++ .../mocks/verifying_message_expecation.rb | 59 +++++ lib/rspec/mocks/verifying_proxy.rb | 84 +++++++ spec/rspec/mocks/arity_calculator_spec.rb | 103 ++++++++ spec/rspec/mocks/verifying_double_spec.rb | 228 ++++++++++++++++++ .../verifying_message_expecation_spec.rb | 78 ++++++ 19 files changed, 1064 insertions(+), 8 deletions(-) create mode 100644 features/verifying_doubles/class_doubles.feature create mode 100644 features/verifying_doubles/dynamic_classes.feature create mode 100644 features/verifying_doubles/introduction.feature create mode 100644 lib/rspec/mocks/arity_calculator.rb create mode 100644 lib/rspec/mocks/module_reference.rb create mode 100644 lib/rspec/mocks/verifying_double.rb create mode 100644 lib/rspec/mocks/verifying_message_expecation.rb create mode 100644 lib/rspec/mocks/verifying_proxy.rb create mode 100644 spec/rspec/mocks/arity_calculator_spec.rb create mode 100644 spec/rspec/mocks/verifying_double_spec.rb create mode 100644 spec/rspec/mocks/verifying_message_expecation_spec.rb diff --git a/Changelog.md b/Changelog.md index a7fe3e114..7af0cfb20 100644 --- a/Changelog.md +++ b/Changelog.md @@ -24,6 +24,8 @@ Enhancements: * Allow the `have_received` matcher to use a block to set further expectations on arguments. (Tim Cowlishaw) * Yield the receiver to `any_instance` implementation blocks (Sam Phippen). +* Provide `instance_double` and `class_double` to create verifying doubles, + ported from `rspec-fire` (Xavier Shay). Bug Fixes: diff --git a/features/.nav b/features/.nav index d974103ac..b90ebd9ef 100644 --- a/features/.nav +++ b/features/.nav @@ -22,6 +22,10 @@ - stub_defined_constant.feature - stub_undefined_constant.feature - hiding_defined_constant.feature +- verifying_doubles: + - introduction.feature + - class_doubles.feature + - dynamic_classes.feature - outside_rspec: - configuration.feature - standalone.feature diff --git a/features/verifying_doubles/class_doubles.feature b/features/verifying_doubles/class_doubles.feature new file mode 100644 index 000000000..6dc2cfdde --- /dev/null +++ b/features/verifying_doubles/class_doubles.feature @@ -0,0 +1,88 @@ +Feature: Using a class double + + `class_double` is provided as a complement to `instance_double`, with the + difference that it verifies class methods on the given class rather than + instance methods. + + In addition, it also provides a convenience method `as_stubbed_const` to + replace concrete classes with the defined double. See [mutating + constants](../mutating-constants) for more details. + + Note: `class_double` can be used for modules as well. We chose to stick with + the `class_double` terminology because the methods a `class_double` verifies + against are commonly called "class methods", not "module methods", even when + working with a module. + + Background: + Given a file named "lib/user.rb" with: + """ruby + class User + def suspend! + ConsoleNotifier.notify("suspended as") + end + end + """ + + Given a file named "lib/console_notifier.rb" with: + """ruby + class ConsoleNotifier + MAX_WIDTH = 80 + + def self.notify(message) + puts message + end + end + """ + + Given a file named "spec/user_spec.rb" with: + """ruby + require 'user' + require 'console_notifier' + + describe User, '#suspend!' do + it 'notifies the console' do + notifier = class_double("ConsoleNotifier"). + as_stubbed_const(:transfer_nested_constants => true) + + expect(notifier).to receive(:notify).with("suspended as") + expect(ConsoleNotifier::MAX_WIDTH).to eq(80) + + user = User.new + user.suspend! + end + end + """ + + Scenario: replacing existing constants + When I run `rspec spec/user_spec.rb` + Then the examples should all pass + + Scenario: renaming `ConsoleNotifier.notify` to `send_notification` + Given a file named "lib/console_notifier.rb" with: + """ruby + class ConsoleNotifier + MAX_WIDTH = 80 + + def self.send_notification(message) + puts message + end + end + """ + When I run `rspec spec/user_spec.rb` + Then the output should contain "1 example, 1 failure" + And the output should contain "ConsoleNotifier does not implement:" + + Scenario: adding `color` as a second argument to `ConsoleNotifier.notify` + Given a file named "lib/console_notifier.rb" with: + """ruby + class ConsoleNotifier + MAX_WIDTH = 80 + + def self.notify(message, color) + puts color + message + end + end + """ + When I run `rspec spec/user_spec.rb` + Then the output should contain "1 example, 1 failure" + And the output should contain "Wrong number of arguments." diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature new file mode 100644 index 000000000..1b30adf4b --- /dev/null +++ b/features/verifying_doubles/dynamic_classes.feature @@ -0,0 +1,69 @@ +Feature: Dynamic classes + + Verifying instance doubles do not support methods which the class reports to + not exist since an actual instance of the class would be required to verify + against. This is commonly the case when `method_missing` is used. + `ActiveRecord` does this to define methods from database columns. + + These types of methods are supported at class level, since `respond_to?` can + be queried directly on the class. + + Background: + Given a file name "lib/fake_active_record.rb" + """ruby + class FakeActiveRecord + COLUMNS = %w[name email] + + def respond_to_missing?(method_name) + COLUMNS.include?(method_name.to_s) || super + end + + def method_missing(method_name, *args) + if respond_to?(method_name) + instance_variable_get("@#{method_name}") + else + super + end + end + end + """ + + Given a file named "spec/user_spec.rb" with: + """ruby + require 'user' + + describe User do + it 'can be doubled' do + instance_double("User", name: "Don") + end + end + """ + + Scenario: fails with method missing + + Given a file named "lib/user.rb" with: + """ruby + require 'fake_active_record' + + class User < FakeActiveRecord + end + """ + + When I run `rspec spec/user_spec.rb` + Then the output should contain "1 example, 1 failure" + + Scenario: workaround with explict definitions + + Given a file named "lib/user.rb" with: + """ruby + require 'fake_active_record' + + class User < FakeActiveRecord + def name; super end + def email; super end + end + """ + + When I run `rspec spec/user_spec.rb` + Then the examples should all pass + diff --git a/features/verifying_doubles/introduction.feature b/features/verifying_doubles/introduction.feature new file mode 100644 index 000000000..863a2bdfc --- /dev/null +++ b/features/verifying_doubles/introduction.feature @@ -0,0 +1,85 @@ +Feature: Verifying doubles + + Verifying doubles are a stricter alternative to normal doubles that provide + guarantees about what is being verified. When using verifying doubles, RSpec + will check that the methods being stubbed are actually present on the + underlying object if it is available. Prefer using veryifing doubles over + normal doubles. + + No checking will happen if the constant name is not defined, but when run + with the constant present (either as a full spec run or by explicitly + preloading collaborators) a failure will be triggered if an invalid method is + being stubbed. + + This dual approach allows you to move very quickly and test components in + isolation, while giving you confidence that your doubles are not a complete + fiction. Testing in isolation is optional but recommend for classes that do + not depend on third-party components. + + Background: + Given a file named "app/models/user.rb" with: + """ruby + class User < Struct.new(:notifier) + def suspend! + notifier.notify("suspended as") + end + end + """ + + Given a file named "app/models/console_notifier.rb" with: + """ruby + class ConsoleNotifier + # notify is not defined yet. + end + """ + + Given a file named "spec/unit_helper.rb" with: + """ruby + $LOAD_PATH.unshift("app/models") + """ + + Given a file named "spec/spec_helper.rb" with: + """ruby + require 'unit_helper' + + require 'user' + require 'console_notifier' + + RSpec.configure do |config| + config.mock_with :rspec do |mocks| + + # This option should be set when all dependencies are being loaded + # before a spec run, as is the case in a typical spec helper. It will + # cause any verifying double instantiation for a class that does not + # exist to raise, protecting against incorrectly spelt names. + mocks.verify_doubled_constant_names = true + + end + end + """ + + Given a file named "spec/unit/user_spec.rb" with: + """ruby + require 'unit_helper' + + require 'user' + + describe User, '#suspend!' do + it 'notifies the console' do + notifier = instance_double("ConsoleNotifier") + + expect(notifier).to receive(:notify).with("suspended as") + + user = User.new(notifier) + user.suspend! + end + end + """ + + Scenario: spec passes in isolation + When I run `rspec spec/unit/user_spec.rb` + Then the examples should all pass + + Scenario: spec fails with dependencies loaded + When I run `rspec -r./spec/spec_helper spec/unit/user_spec.rb` + Then the output should contain "1 example, 1 failure" diff --git a/lib/rspec/mocks/arity_calculator.rb b/lib/rspec/mocks/arity_calculator.rb new file mode 100644 index 000000000..7a53d8c3e --- /dev/null +++ b/lib/rspec/mocks/arity_calculator.rb @@ -0,0 +1,71 @@ +module RSpec + module Mocks + + # Figures out the valid arity range for a method. Surprisingly non-trivial. + class ArityCalculator + + # Default value for method if it not available. + MethodNotLoaded = Class.new + + def initialize(method) + @method = method + end + + # @api private + def within_range?(actual) + return true if method == MethodNotLoaded + + min_arity <= actual && actual <= max_arity + end + + # @api private + def range_description + return min_arity.to_s if min_arity == max_arity + return "#{min_arity} or more" if max_arity == INFINITY + "#{min_arity} to #{max_arity}" + end + + private + + def method + @method + end + + # @api private + def self.supports_optional_and_splat_args? + Method.method_defined?(:parameters) + end + + def min_arity + return method.arity if method.arity >= 0 + # `~` inverts the one's complement and gives us the number of + # required arguments. + ~method.arity + end + + if supports_optional_and_splat_args? + def max_arity + params = method.parameters + if params.any? {|(type, _)| type == :rest } + # Method takes a splat argument + return INFINITY + else + params.count {|(type, _)| type != :block } + end + end + else + def max_arity + # On 1.8, Method#parameters does not exist. There is no way to + # distinguish between default and splat args, so there is no way to + # have it work correctly for both default and splat args, as far as I + # can tell. The best we can do is consider it INFINITY (to be + # tolerant of splat args). + method.arity < 0 ? INFINITY : method.arity + end + end + + INFINITY = 1/0.0 + end + end +end + diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index bd9626051..48dc82064 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -5,6 +5,7 @@ class Configuration def initialize @yield_receiver_to_any_instance_implementation_blocks = true + @verify_doubled_constant_names = false end def yield_receiver_to_any_instance_implementation_blocks? @@ -56,6 +57,19 @@ def syntax syntaxes << :expect if Syntax.expect_enabled? syntaxes end + + def verify_doubled_constant_names? + !!@verify_doubled_constant_names + end + + # When this is set to true, an error will be raised when + # `instance_double` or `class_double` is given the name of an undefined + # constant. You probably only want to set this when running your entire + # test suite, with all production code loaded. Setting this for an + # isolated unit test will prevent you from being able to isolate it! + def verify_doubled_constant_names=(val) + @verify_doubled_constant_names = val + end end def self.configuration diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index b51a8e656..605e5cbbb 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -47,6 +47,22 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc __raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}" end + # @private + def raise_unimplemented_error(doubled_module, method_name) + __raise "%s does not implement:\n %s" % [ + doubled_module.name, + method_name + ] + end + + # @private + def raise_arity_error(calculator, actual) + __raise "Wrong number of arguments. Expected %s, got %s." % [ + calculator.range_description, + actual + ] + end + # @private def received_part_of_expectation_error(actual_received_count, *args) "received: #{count_message(actual_received_count)}" + diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index b7702a4b6..e0af9499f 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -1,3 +1,5 @@ +require 'rspec/mocks/module_reference' + module RSpec module Mocks module ExampleMethods @@ -9,12 +11,12 @@ module ExampleMethods # @overload double(name, stubs) # @param name [String/Symbol] (optional) used in # clarify intent - # @param stubs (Hash) (optional) hash of method/return-value pairs + # @param stubs (Hash) (optional) hash of message/return-value pairs # @return (Mock) # # Constructs an instance of [RSpec::Mocks::Mock](RSpec::Mocks::Mock) configured # with an optional name, used for reporting in failure messages, and an optional - # hash of method/return-value pairs. + # hash of message/return-value pairs. # # @example # @@ -26,8 +28,35 @@ module ExampleMethods # card.rank #=> "A" # def double(*args) - args << {} unless Hash === args.last - RSpec::Mocks::Mock.new(*args) + declare_double(Mock, *args) + end + + # @overload instance_double(doubled_class) + # @overload instance_double(doubled_class, stubs) + # @param doubled_class [String, Class] + # @param stubs [Hash] (optional) hash of message/return-value pairs + # @return InstanceVerifyingMock + # + # Constructs a test double against a specific class. If the given class + # name has been loaded, only instance methods defined on the class are + # allowed to be stubbed. In all other ways it behaves like a + # [double](double). + def instance_double(doubled_class, *args) + declare_verifying_double(InstanceVerifyingMock, doubled_class, *args) + end + + # @overload instance_double(doubled_class) + # @overload instance_double(doubled_class, stubs) + # @param doubled_class [String, Module] + # @param stubs [Hash] (optional) hash of message/return-value pairs + # @return ClassVerifyingMock + # + # Constructs a test double against a specific class. If the given class + # name has been loaded, only class methods defined on the class are + # allowed to be stubbed. In all other ways it behaves like a + # [double](double). + def class_double(doubled_class, *args) + declare_verifying_double(ClassVerifyingMock, doubled_class, *args) end # Disables warning messages about expectations being set on nil. @@ -127,6 +156,27 @@ def self.included(klass) end private + + def declare_verifying_double(type, constant_or_name, *args) + ref = ModuleReference.new(constant_or_name) + + if RSpec::Mocks.configuration.verify_doubled_constant_names? && + !ref.defined? + + raise NameError, + "#{ref.name} is not a defined constant. " + + "Perhaps you misspelt it? " + + "Disable check with verify_constant_names configuration option." + end + + declare_double(type, ref, *args) + end + + def declare_double(type, *args) + args << {} unless Hash === args.last + type.new(*args) + end + # This module exists to host the `expect` method for cases where # rspec-mocks is used w/o rspec-expectations. module ExpectHost diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 4665ea470..2e776824c 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -31,3 +31,4 @@ require 'rspec/mocks/targets' require 'rspec/mocks/syntax' require 'rspec/mocks/configuration' +require 'rspec/mocks/verifying_double' diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 45c0bcae1..9830590f8 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -212,10 +212,18 @@ def clear stubs.clear end + # The type of message expectation to create has been extracted to its own + # method so that subclasses can override it. + # + # @private + def message_expectation_class + MessageExpectation + end + # @private def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation) configure_method - expectation = MessageExpectation.new(error_generator, expectation_ordering, + expectation = message_expectation_class.new(error_generator, expectation_ordering, expected_from, self, 1, opts, &implementation) expectations << expectation expectation @@ -224,13 +232,13 @@ def add_expectation(error_generator, expectation_ordering, expected_from, opts, # @private def build_expectation(error_generator, expectation_ordering) expected_from = IGNORED_BACKTRACE_LINE - MessageExpectation.new(error_generator, expectation_ordering, expected_from, self) + message_expectation_class.new(error_generator, expectation_ordering, expected_from, self) end # @private def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation) configure_method - stub = MessageExpectation.new(error_generator, expectation_ordering, expected_from, + stub = message_expectation_class.new(error_generator, expectation_ordering, expected_from, self, :any, opts, &implementation) stubs.unshift stub stub diff --git a/lib/rspec/mocks/module_reference.rb b/lib/rspec/mocks/module_reference.rb new file mode 100644 index 000000000..6b8faa970 --- /dev/null +++ b/lib/rspec/mocks/module_reference.rb @@ -0,0 +1,37 @@ +module RSpec + module Mocks + + # Provides a consistent interface for dealing with modules that may or may + # not be defined. + # + # @private + class ModuleReference + def initialize(module_or_name) + case module_or_name + when Module then @module = module_or_name + when String then @name = module_or_name + else raise ArgumentError, + "Module or String expected, got #{module_or_name.inspect}" + end + end + + def defined? + !!original_module + end + + def name + @name ||= @module.name + end + + def when_loaded(&block) + yield original_module if original_module + end + + private + + def original_module + @module ||= Constant.original(@name).original_value + end + end + end +end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 48472aa04..60c61f2e1 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -172,12 +172,14 @@ def raise_missing_default_stub_error(expectation, *args) @error_generator.raise_missing_default_stub_error(expectation, *args) end - private + protected def method_double @method_double ||= Hash.new {|h,k| h[k] = MethodDouble.new(@object, k, self) } end + private + def method_doubles method_double.values end diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb new file mode 100644 index 000000000..c45643205 --- /dev/null +++ b/lib/rspec/mocks/verifying_double.rb @@ -0,0 +1,57 @@ +require 'rspec/mocks/mock' +require 'rspec/mocks/verifying_proxy' + +module RSpec + module Mocks + + # A mock providing a custom proxy that can verify the validity of any + # method stubs or expectations against the public instance methods of the + # given class. + class InstanceVerifyingMock + include TestDouble + + def initialize(doubled_module, *args) + @doubled_module = doubled_module + + __initialize_as_test_double(doubled_module, *args) + end + + def __build_mock_proxy + VerifyingProxy.new(self, + @doubled_module, + :method_defined?, + :instance_method + ) + end + end + + # Similar to an InstanceVerifyingMock, except that it verifies against + # public methods of the given class (i.e. the "class methods"). + # + # Module needs to be in the inheritance chain for transferring nested + # constants to work. + class ClassVerifyingMock < Module + include TestDouble + + def initialize(doubled_module, *args) + @doubled_module = doubled_module + + __initialize_as_test_double(doubled_module, *args) + end + + def __build_mock_proxy + VerifyingProxy.new(self, + @doubled_module, + :respond_to?, + :method + ) + end + + def as_stubbed_const(options = {}) + ConstantMutator.stub(@doubled_module.name, self, options) + self + end + end + + end +end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb new file mode 100644 index 000000000..018571a2c --- /dev/null +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -0,0 +1,59 @@ +require 'rspec/mocks/arity_calculator' + +module RSpec + module Mocks + + # A message expectation that knows about the real implementation of the + # message being expected, so that it can verify that any expectations + # have the correct arity. + class VerifyingMessageExpectation < MessageExpectation + + # A level of indirection is used here rather than just passing in the + # method itself, since method look up is expensive and we only want to + # do it if actually needed. + # + # Conceptually the method finder makes more sense as a constructor + # argument since it should be immutable, but it is significantly more + # straight forward to build the object in pieces so for now it stays as + # an accessor. + attr_accessor :method_finder + + def initialize(*args) + super + @method_finder = Proc.new { ArityCalculator::MethodNotLoaded } + end + + # @override + def with(*args, &block) + unless ArgumentMatchers::AnyArgsMatcher === args.first + expected_arity = if block + block.arity + elsif ArgumentMatchers::NoArgsMatcher === args.first + 0 + elsif args.length > 0 + args.length + else + raise ArgumentError, "No arguments nor block given." + end + + ensure_arity!(expected_arity) + end + super + end + + private + + def ensure_arity!(actual) + calculator = ArityCalculator.new(method_finder.call(message)) + + unless calculator.within_range?(actual) + # Fail fast is required, otherwise the message expecation will fail + # as well ("expected method not called") and clobber this one. + @failed_fast = true + @error_generator.raise_arity_error(calculator, actual) + end + end + end + end +end + diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb new file mode 100644 index 000000000..e95f1f713 --- /dev/null +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -0,0 +1,84 @@ +require 'rspec/mocks/verifying_message_expecation' + +module RSpec + module Mocks + + # A verifying proxy mostly acts like a normal proxy, except that it + # contains extra logic to try and determine the validity of any expectation + # set on it. This includes whether or not methods have been defined and the + # arity of method calls. + # + # In all other ways this behaves like a normal proxy. It only adds the + # verification behaviour to specific methods then delegates to the parent + # implementation. + # + # These checks are only activated if the doubled class has already been + # loaded, otherwise they are disabled. This allows for testing in + # isolation. + # + # @api private + class VerifyingProxy < Proxy + def initialize(object, name, method_checker, method_finder) + super(object) + @object = object + @doubled_module = name + @method_checker = method_checker + @method_finder = method_finder + end + + def add_stub(location, method_name, opts={}, &implementation) + ensure_implemented(method_name) + super + end + + def add_message_expectation(location, method_name, opts={}, &block) + ensure_implemented(method_name) + super + end + + # A custom method double is required to pass through a way to lookup + # methods to determine their arity. This is only relevant if the doubled + # class is loaded. + def method_double + @method_double ||= Hash.new do |h,k| + method_double = VerifyingMethodDouble.new(@object, k, self) + + @doubled_module.when_loaded do |original_module| + method_double.method_finder = lambda do |method_name| + original_module.__send__(@method_finder, method_name) + end + end + + h[k] = method_double + end + end + + protected + + def ensure_implemented(method_name) + @doubled_module.when_loaded do |original_module| + unless original_module.__send__(@method_checker, method_name) + @error_generator.raise_unimplemented_error( + @doubled_module, + method_name + ) + end + end + end + end + + # @api private + class VerifyingMethodDouble < MethodDouble + attr_accessor :method_finder + + def message_expectation_class + VerifyingMessageExpectation + end + + def add_expectation(*arg) + super.tap {|x| x.method_finder = method_finder if method_finder } + end + end + + end +end diff --git a/spec/rspec/mocks/arity_calculator_spec.rb b/spec/rspec/mocks/arity_calculator_spec.rb new file mode 100644 index 000000000..cf9f0b297 --- /dev/null +++ b/spec/rspec/mocks/arity_calculator_spec.rb @@ -0,0 +1,103 @@ +require 'spec_helper' + +module RSpec + module Mocks + describe ArityCalculator do + describe '#verify!' do + subject { described_class.new(test_method) } + + def within_range?(arity) + subject.within_range?(arity) + end + + def description + subject.range_description + end + + describe 'with unloaded method' do + let(:test_method) { ArityCalculator::MethodNotLoaded } + + it 'covers any range' do + expect(within_range?(123)).to eq(true) + end + end + + describe 'with a method with arguments' do + def arity_two(x, y); end + + let(:test_method) { method(:arity_two) } + + it 'covers only the exact arity' do + expect(within_range?(1)).to eq(false) + expect(within_range?(2)).to eq(true) + expect(within_range?(3)).to eq(false) + end + + it 'is described precisely' do + expect(description).to eq("2") + end + end + + describe 'a method with splat arguments' do + def arity_splat(_, *); end + + let(:test_method) { method(:arity_splat) } + + it 'covers a range from the lower bound upwards' do + expect(within_range?(0)).to eq(false) + expect(within_range?(1)).to eq(true) + expect(within_range?(2)).to eq(true) + expect(within_range?(3)).to eq(true) + end + + it 'is described with no upper bound' do + expect(description).to eq("1 or more") + end + end + + describe 'a method with optional arguments' do + def arity_optional(x, y, z = 1); end + + let(:test_method) { method(:arity_optional) } + + it 'covers a range from min to max possible arguments' do + expect(within_range?(1)).to eq(false) + expect(within_range?(2)).to eq(true) + expect(within_range?(3)).to eq(true) + + if ArityCalculator.supports_optional_and_splat_args? + expect(within_range?(4)).to eq(false) + else + expect(within_range?(4)).to eq(true) + end + end + + if ArityCalculator.supports_optional_and_splat_args? + it 'is described as a range' do + expect(description).to eq("2 to 3") + end + else + it 'is described with no upper bound' do + expect(description).to eq("2 or more") + end + end + end + + describe 'a method with a block' do + def arity_block(_, &block); end + + let(:test_method) { method(:arity_block) } + + it 'does not count the block as a parameter' do + expect(within_range?(1)).to eq(true) + expect(within_range?(2)).to eq(false) + end + + it 'is described precisely' do + expect(description).to eq("1") + end + end + end + end + end +end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb new file mode 100644 index 000000000..6f36d2d5d --- /dev/null +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -0,0 +1,228 @@ +require 'spec_helper' + +class LoadedClass + M = :m + N = :n + + def defined_instance_method; end + def self.defined_class_method; end + + def respond_to?(method_name) + return true if method_name == :dynamic_instance_method + super + end + + def self.respond_to?(method_name) + return true if method_name == :dynamic_class_method + super + end + + def self.send + # fake out! + end + + class Nested; end +end + +module RSpec + module Mocks + describe 'verifying doubles' do + def prevents(&block) + expect(&block).to \ + raise_error(RSpec::Mocks::MockExpectationError) + end + + def verifying_constant_names(bool, &block) + config = RSpec::Mocks.configuration + + original_value = config.verify_doubled_constant_names? + begin + config.verify_doubled_constant_names = bool + block.call + ensure + config.verify_doubled_constant_names = original_value + end + end + + describe 'instance doubles' do + describe 'when doubled class is not loaded' do + around do |example| + verifying_constant_names(false) do + example.run + end + end + + it 'allows any instance method to be stubbed' do + o = instance_double('NonloadedClass') + o.stub(:undefined_instance_method).with(:arg).and_return(true) + expect(o.undefined_instance_method(:arg)).to eq(true) + end + + it 'allows any instance method to be expected' do + o = instance_double("NonloadedClass") + + expect(o).to receive(:undefined_instance_method). + with(:arg). + and_return(true) + + expect(o.undefined_instance_method(:arg)).to eq(true) + end + end + + describe 'when doubled class is loaded' do + around do |example| + verifying_constant_names(true) do + example.run + end + end + + it 'only allows instance methods that exist to be stubbed' do + o = instance_double('LoadedClass', :defined_instance_method => 1) + expect(o.defined_instance_method).to eq(1) + + prevents { o.stub(:undefined_instance_method) } + prevents { o.stub(:defined_class_method) } + end + + it 'only allows instance methods that exist to be expected' do + o = instance_double('LoadedClass') + expect(o).to receive(:defined_instance_method) + o.defined_instance_method + + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_class_method) } + prevents { o.should_receive(:undefined_instance_method) } + prevents { o.should_receive(:defined_class_method) } + end + + it 'does not allow dynamic methods to be expected' do + # This isn't possible at the moment since an instance of the class + # would be required for the verification, and we only have the + # class itself. + # + # This spec exists as "negative" documentation of the absence of a + # feature, to highlight the asymmetry from class doubles (that do + # support this behaviour). + prevents { + instance_double('LoadedClass', :dynamic_instance_method => 1) + } + end + + it 'checks the arity of stubbed methods' do + o = instance_double('LoadedClass') + prevents { + expect(o).to receive(:defined_instance_method).with(:a) + } + end + + it 'allows class to be specified by constant' do + o = instance_double(LoadedClass, :defined_instance_method => 1) + expect(o.defined_instance_method).to eq(1) + end + end + end + + describe 'class doubles' do + describe 'when doubled class is not loaded' do + around do |example| + verifying_constant_names(false) do + example.run + end + end + + it 'allows any method to be stubbed' do + o = class_double('NonloadedClass') + o.stub(:undefined_instance_method).with(:arg).and_return(1) + expect(o.undefined_instance_method(:arg)).to eq(1) + end + end + + describe 'when doubled class is loaded' do + around do |example| + verifying_constant_names(true) do + example.run + end + end + + it 'only allows class methods that exist to be stubbed' do + o = class_double('LoadedClass', :defined_class_method => 1) + expect(o.defined_class_method).to eq(1) + + prevents { o.stub(:undefined_instance_method) } + prevents { o.stub(:defined_instance_method) } + end + + it 'only allows class methods that exist to be expected' do + o = class_double('LoadedClass') + expect(o).to receive(:defined_class_method) + o.defined_class_method + + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_instance_method) } + prevents { o.should_receive(:undefined_instance_method) } + prevents { o.should_receive(:defined_instance_method) } + end + + it 'allows dynamically defined class methods to be expected' do + o = class_double('LoadedClass', :dynamic_class_method => 1) + expect(o.dynamic_class_method).to eq(1) + end + + it 'allows class to be specified by constant' do + o = class_double(LoadedClass, :defined_class_method => 1) + expect(o.defined_class_method).to eq(1) + end + + it 'can replace existing constants for the duration of the test' do + original = LoadedClass + object = class_double('LoadedClass').as_stubbed_const + expect(object).to receive(:defined_class_method) + + expect(LoadedClass).to eq(object) + ::RSpec::Mocks.space.reset_all + expect(LoadedClass).to eq(original) + end + + it 'can transfer nested constants to the double' do + class_double("LoadedClass"). + as_stubbed_const(:transfer_nested_constants => true) + expect(LoadedClass::M).to eq(:m) + expect(LoadedClass::N).to eq(:n) + end + + it 'correctly verifies expectations when constant is removed' do + dbl1 = class_double(LoadedClass::Nested).as_stubbed_const + dbl2 = class_double(LoadedClass).as_stubbed_const + + prevents { + expect(dbl1).to receive(:undefined_class_method) + } + end + end + end + + describe 'when verify_doubled_constant_names config option is set' do + it 'prevents creation of instance doubles for unloaded constants' do + verifying_constant_names(true) do + expect { + instance_double('LoadedClas') + }.to raise_error(NameError) + end + end + + it 'prevents creation of class doubles for unloaded constants' do + verifying_constant_names(true) do + expect { + class_double('LoadedClas') + }.to raise_error(NameError) + end + end + end + + it 'can only be named with a string or a module' do + expect { instance_double(1) }.to raise_error(ArgumentError) + expect { instance_double(nil) }.to raise_error(ArgumentError) + end + end + end +end diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb new file mode 100644 index 000000000..39236f156 --- /dev/null +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +module RSpec + module Mocks + describe VerifyingMessageExpectation do + describe '#with' do + let(:method_tracer) { Object.new } + let(:error_generator) { double.as_null_object } + + subject { + null = double.as_null_object + + described_class.new(error_generator, null, null, null) + } + + describe 'when expected method is not loaded' do + it 'allows any arguments to be expected' do + subject.with(:foo, :bar) + end + end + + describe 'when arity match fails' do + it 'raises error' do + subject.method_finder = Proc.new { lambda {|_| } } + expect(error_generator).to receive(:raise_arity_error). + with(instance_of(ArityCalculator), 2) + + subject.with(nil, nil) + end + end + + describe 'when called with arguments' do + it 'matches arity against the number of arguments' do + subject.method_finder = Proc.new { lambda {|_| } } + expect(error_generator).not_to receive(:raise_arity_error) + + subject.with(nil) + end + end + + describe 'when called with any arguments matcher' do + it 'does not try to match arity' do + subject.method_finder = Proc.new { raise } + subject.with(any_args) + end + end + + describe 'when called with no arguments matcher' do + it 'matches arity to 0' do + subject.method_finder = Proc.new { lambda {|_| } } + expect(error_generator).to receive(:raise_arity_error). + with(instance_of(ArityCalculator), 0) + + subject.with(no_args) + end + end + + describe 'when called with a block' do + it 'matches arity against the arity of the block' do + subject.method_finder = Proc.new { lambda {|_| } } + expect(error_generator).to receive(:raise_arity_error). + with(instance_of(ArityCalculator), 2) + + subject.with {|x, y| } + end + end + + describe 'when called with no arguments and no block' do + it 'raises' do + expect { + subject.with + }.to raise_error(ArgumentError, "No arguments nor block given.") + end + end + end + end + end +end From 54929e336b105502184f577ace6cd56a3a4bf6d7 Mon Sep 17 00:00:00 2001 From: Michael Gee Date: Tue, 6 Aug 2013 20:56:51 -0400 Subject: [PATCH 0142/1179] copypasta mistake in class_double docs --- lib/rspec/mocks/example_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index e0af9499f..ddfbeddb5 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -45,8 +45,8 @@ def instance_double(doubled_class, *args) declare_verifying_double(InstanceVerifyingMock, doubled_class, *args) end - # @overload instance_double(doubled_class) - # @overload instance_double(doubled_class, stubs) + # @overload class_double(doubled_class) + # @overload class_double(doubled_class, stubs) # @param doubled_class [String, Module] # @param stubs [Hash] (optional) hash of message/return-value pairs # @return ClassVerifyingMock From e46e4fecf79323a0c542c68f1fea2347a230e3ba Mon Sep 17 00:00:00 2001 From: Bradley Schaefer Date: Wed, 7 Aug 2013 14:47:46 -0500 Subject: [PATCH 0143/1179] Combine mocking on any instance cukes --- .../message_expectations/any_instance.feature | 30 ++++++++++++++++--- .../expect_any_instance_of.feature | 27 ----------------- 2 files changed, 26 insertions(+), 31 deletions(-) delete mode 100644 features/message_expectations/expect_any_instance_of.feature diff --git a/features/message_expectations/any_instance.feature b/features/message_expectations/any_instance.feature index fb3239bc0..d73863b9b 100644 --- a/features/message_expectations/any_instance.feature +++ b/features/message_expectations/any_instance.feature @@ -1,11 +1,33 @@ Feature: expect a message on any instance of a class - Use `any_instance.should_receive` to set an expectation that one (and only - one) instance of a class receives a message before the example is completed. + Use `expect_any_instance_of(Class).to receive` to set an expectation that one + (and only one) instance of a class receives a message before the example is + completed. - The spec will fail if no instance receives a message. + The example will fail if no instance receives the specified message. Scenario: expect a message on any instance of a class + Given a file named "example_spec.rb" with: + """ruby + describe "expect_any_instance_of" do + before do + expect_any_instance_of(Object).to receive(:foo).and_return(:return_value) + end + + it "verifies that one instance of the class receives the message" do + o = Object.new + o.foo.should eq(:return_value) + end + + it "fails unless an instance receives that message" do + end + end + """ + When I run `rspec example_spec.rb` + Then the output should contain "2 examples, 1 failure" + And the output should contain "1) expect_any_instance_of fails unless an instance receives that message" + + Scenario: expect a message on any instance of a class (alternate syntax) Given a file named "example_spec.rb" with: """ruby describe "any_instance.should_receive" do @@ -18,4 +40,4 @@ Feature: expect a message on any instance of a class end """ When I run `rspec example_spec.rb` - Then the examples should all pass \ No newline at end of file + Then the examples should all pass diff --git a/features/message_expectations/expect_any_instance_of.feature b/features/message_expectations/expect_any_instance_of.feature deleted file mode 100644 index b0b061814..000000000 --- a/features/message_expectations/expect_any_instance_of.feature +++ /dev/null @@ -1,27 +0,0 @@ -Feature: expect a message on any instance of a class - - Use `expect_any_instance_of(Class).to receive` to set an expectation that one - (and only one) instance of a class receives a message before the example is - completed. The spec will fail if no instance receives a message. - - Scenario: expect a message on any instance of a class - Given a file named "example_spec.rb" with: - """ruby - describe "expect_any_instance_of" do - before do - expect_any_instance_of(Object).to receive(:foo).and_return(:return_value) - end - - it "verifies that one instance of the class receives the message" do - o = Object.new - expect(o.foo).to eq(:return_value) - end - - it "fails unless an instance receives that message" do - o = Object.new - end - end - """ - When I run `rspec example_spec.rb` - Then the output should contain "2 examples, 1 failure" - And the output should contain "1) expect_any_instance_of fails unless an instance receives that message" From 0d07799af027791e0e8679f6cea5543ecf7e1089 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Aug 2013 23:26:36 -0700 Subject: [PATCH 0144/1179] Update changelog for 2.14.3 release. [ci skip] --- Changelog.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 7af0cfb20..1595788bc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### Development -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...master) +[full changelog](http://github.com/rspec/rspec-mocks/compare/2-99-maintenance...master) Breaking Changes for 3.0.0: @@ -27,6 +27,9 @@ Enhancements: * Provide `instance_double` and `class_double` to create verifying doubles, ported from `rspec-fire` (Xavier Shay). +### 2.14.3 / 2013-08-08 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) + Bug Fixes: * Fix stubbing some instance methods for classes whose hierarchy includes From d483e0a893d97c7b8e612e878a9f3562a210df9f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Aug 2013 23:48:58 -0700 Subject: [PATCH 0145/1179] Update copyright and gemspec authors. [ci skip] --- License.txt | 1 + rspec-mocks.gemspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/License.txt b/License.txt index 84edafa27..91cfc94f6 100644 --- a/License.txt +++ b/License.txt @@ -1,5 +1,6 @@ (The MIT License) +Copyright (c) 2012 David Chelimsky, Myron Marston Copyright (c) 2006 David Chelimsky, The RSpec Development Team Copyright (c) 2005 Steven Baker diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 52f83d943..3a0f0f673 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.version = RSpec::Mocks::Version::STRING s.platform = Gem::Platform::RUBY s.license = "MIT" - s.authors = ["Steven Baker", "David Chelimsky"] + s.authors = ["Steven Baker", "David Chelimsky", "Myron Marston"] s.email = "rspec-users@rubyforge.org" s.homepage = "http://github.com/rspec/rspec-mocks" s.summary = "rspec-mocks-#{RSpec::Mocks::Version::STRING}" From 37519536cc96f6ecfdd69a4d5353714ad126ac69 Mon Sep 17 00:00:00 2001 From: Bradley Schaefer Date: Fri, 9 Aug 2013 08:48:40 -0500 Subject: [PATCH 0146/1179] Use expect syntax more consistently --- features/message_expectations/any_instance.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/message_expectations/any_instance.feature b/features/message_expectations/any_instance.feature index d73863b9b..26d7eb3f0 100644 --- a/features/message_expectations/any_instance.feature +++ b/features/message_expectations/any_instance.feature @@ -16,7 +16,7 @@ Feature: expect a message on any instance of a class it "verifies that one instance of the class receives the message" do o = Object.new - o.foo.should eq(:return_value) + expect(o.foo).to eq(:return_value) end it "fails unless an instance receives that message" do @@ -27,7 +27,7 @@ Feature: expect a message on any instance of a class Then the output should contain "2 examples, 1 failure" And the output should contain "1) expect_any_instance_of fails unless an instance receives that message" - Scenario: expect a message on any instance of a class (alternate syntax) + Scenario: expect a message on any instance of a class (should syntax) Given a file named "example_spec.rb" with: """ruby describe "any_instance.should_receive" do From 66e17f39cbb239f00a84ef11cad6c8589aff0946 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 10 Aug 2013 01:16:02 -0300 Subject: [PATCH 0147/1179] Moving double inspect spec to general double suite --- spec/rspec/mocks/bug_report_10260_spec.rb | 8 -------- spec/rspec/mocks/double_spec.rb | 5 +++++ 2 files changed, 5 insertions(+), 8 deletions(-) delete mode 100644 spec/rspec/mocks/bug_report_10260_spec.rb diff --git a/spec/rspec/mocks/bug_report_10260_spec.rb b/spec/rspec/mocks/bug_report_10260_spec.rb deleted file mode 100644 index ab2fb77b3..000000000 --- a/spec/rspec/mocks/bug_report_10260_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'spec_helper' - -describe "An RSpec Mock" do - it "hides internals in its inspect representation" do - m = double('cup') - expect(m.inspect).to match(/#/) - end -end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index c9d5ec1cc..422234506 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -9,4 +9,9 @@ double = double('name') expect {double.foo}.to raise_error(/Double "name" received/) end + + it "hides internals in its inspect representation" do + m = double('cup') + expect(m.inspect).to match(/#/) + end end From 8dcb3c9123af6540769676a66afc918f9e61a2f0 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 10 Aug 2013 08:59:59 -0300 Subject: [PATCH 0148/1179] Removing bug report 11545. It covers the same features as partial_mock_using_mocks_directly and verifying_double_spec --- spec/rspec/mocks/bug_report_11545_spec.rb | 32 ----------------------- 1 file changed, 32 deletions(-) delete mode 100644 spec/rspec/mocks/bug_report_11545_spec.rb diff --git a/spec/rspec/mocks/bug_report_11545_spec.rb b/spec/rspec/mocks/bug_report_11545_spec.rb deleted file mode 100644 index 73ec05c37..000000000 --- a/spec/rspec/mocks/bug_report_11545_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'spec_helper' - -class LiarLiarPantsOnFire - def respond_to?(message, incl_private=false) - true - end - - def self.respond_to?(message, incl_private=false) - true - end -end - -describe 'should_receive' do - before(:each) do - @liar = LiarLiarPantsOnFire.new - end - - it "works when object lies about responding to a method" do - @liar.should_receive(:something) - @liar.something - end - - it 'works when class lies about responding to a method' do - LiarLiarPantsOnFire.should_receive(:something) - LiarLiarPantsOnFire.something - end - - it 'cleans up after itself' do - expect((class << LiarLiarPantsOnFire; self; end).instance_methods).not_to include("something") - end -end - From 860d591a4bb22f4e213a980372102a94780eb61e Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 3 Aug 2013 09:37:40 -0700 Subject: [PATCH 0149/1179] Speed up double creation with multiple attributes by caching caller. Benchmark results for creating a double with n attributes: n | before | after ---+--------+------- 0 | 0.01 | 0.04 1 | 0.11 | 0.11 2 | 0.24 | 0.16 3 | 0.39 | 0.22 4 | 0.62 | 0.29 5 | 0.88 | 0.35 6 | 1.34 | 0.51 7 | 1.77 | 0.51 8 | 2.59 | 0.72 9 | 3.33 | 0.84 --- lib/rspec/mocks/test_double.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 2c01060ef..bad247f66 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -96,8 +96,13 @@ def method_missing(message, *args, &block) end def assign_stubs(stubs) + # Performance optimization so that `caller` is not called needlessly. + return if stubs.empty? + + opts = {:expected_from => caller(1)[0]} + stubs.each_pair do |message, response| - Mocks.allow_message(self, message).and_return(response) + Mocks.allow_message(self, message, opts).and_return(response) end end From 4a04b3e831bb633259fff7b9ad70acce954bca81 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 3 Aug 2013 10:31:49 -0700 Subject: [PATCH 0150/1179] Extract constant ArgumentListMatcher::MATCH_ALL. As well as being semantically clearer, this gives a modest speed up (~5%) when creating doubles with multiple attributes. --- lib/rspec/mocks/argument_list_matcher.rb | 7 +++++++ lib/rspec/mocks/message_expectation.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index ee17eb946..d9e6bf421 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -23,6 +23,8 @@ module Mocks # arg_list_matcher = RSpec::Mocks::ArgumentListMatcher.new(123, hash_including(:a => 'b')) # arg_list_matcher.args_match?(123, :a => 'b') # + # This class is immutable. + # # @see ArgumentMatchers class ArgumentListMatcher # @private @@ -92,6 +94,11 @@ def matchers_match?(*args) def match_any_args? @match_any_args end + + # Value that will match all argument lists. + # + # @private + MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher.new) end end end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index cfd556162..f4767977b 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -20,7 +20,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @message = @method_double.method_name @actual_received_count = 0 @expected_received_count = expected_received_count - @argument_list_matcher = ArgumentListMatcher.new(ArgumentMatchers::AnyArgsMatcher.new) + @argument_list_matcher = ArgumentListMatcher::MATCH_ALL @order_group = expectation_ordering @at_least = @at_most = @exactly = nil @args_to_yield = [] From 46c1eb0e90882a9db1a8c24031d562d5c8019df5 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 3 Aug 2013 11:32:37 -0700 Subject: [PATCH 0151/1179] Introduce "simple" stub as an optimization over using a normal stub. This provides a decent speed improvement when creating doubles with multiple attributes: n | before | after ---+--------+------- 0 | 0.04 | 0.04 1 | 0.09 | 0.08 2 | 0.17 | 0.12 3 | 0.20 | 0.16 4 | 0.26 | 0.18 5 | 0.32 | 0.23 6 | 0.40 | 0.29 7 | 0.55 | 0.40 8 | 0.65 | 0.40 9 | 0.78 | 0.46 --- lib/rspec/mocks/message_expectation.rb | 14 ++++++++++++++ lib/rspec/mocks/method_double.rb | 15 +++++++++++++++ lib/rspec/mocks/proxy.rb | 4 ++++ lib/rspec/mocks/test_double.rb | 4 ++-- lib/rspec/mocks/verifying_proxy.rb | 5 +++++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index f4767977b..27cb53282 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -1,6 +1,20 @@ module RSpec module Mocks + # A message expectation that only allows concrete return values to be set + # for a message. While this same effect can be achieved using a standard + # MessageExpecation, this version is much faster and so can be used as an + # optimization. + SimpleMessageExpectation = Struct.new(:message, :response) do + def invoke(*_) + response + end + + def matches?(message, *_) + self.message == message + end + end + class MessageExpectation # @private attr_accessor :error_generator, :implementation diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 9830590f8..a84f991bf 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -244,6 +244,21 @@ def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &imp stub end + # A simple stub can only return a concrete value for a message, and + # cannot match on arguments. It is used as an optimization over + # `add_stub` where it is known in advance that this is all that will be + # required of a stub, such as when passing attributes to the `double` + # example method. + # + # @private + def add_simple_stub(error_generator, expectation_ordering, expected_from, method_name, response) + define_proxy_method + + stub = SimpleMessageExpectation.new(method_name, response) + stubs.unshift stub + stub + end + # @private def add_default_stub(*args, &implementation) return if stubs.any? diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 60c61f2e1..ce2ccd3b9 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -97,6 +97,10 @@ def add_stub(location, method_name, opts={}, &implementation) method_double[method_name].add_stub @error_generator, @expectation_ordering, location, opts, &implementation end + def add_simple_stub(location, method_name, response) + method_double[method_name].add_simple_stub @error_generator, @expectation_ordering, location, method_name, response + end + # @private def remove_stub(method_name) method_double[method_name].remove_stub diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index bad247f66..16391c9f3 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -99,10 +99,10 @@ def assign_stubs(stubs) # Performance optimization so that `caller` is not called needlessly. return if stubs.empty? - opts = {:expected_from => caller(1)[0]} + expected_from = caller(1)[0] stubs.each_pair do |message, response| - Mocks.allow_message(self, message, opts).and_return(response) + __mock_proxy.add_simple_stub(expected_from, message, response) end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index e95f1f713..2a43e32e0 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -31,6 +31,11 @@ def add_stub(location, method_name, opts={}, &implementation) super end + def add_simple_stub(_, method_name, *args) + ensure_implemented(method_name) + super + end + def add_message_expectation(location, method_name, opts={}, &block) ensure_implemented(method_name) super From 009a6976f890038c2827df21a9b0f82a90ade614 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 10 Aug 2013 08:54:06 -0700 Subject: [PATCH 0152/1179] Extract CallerFilter class to unify caller manipulation. --- benchmarks/caller.rb | 81 ++++++++++++++++++++++++++++++++ lib/rspec/mocks.rb | 8 +++- lib/rspec/mocks/caller_filter.rb | 58 +++++++++++++++++++++++ lib/rspec/mocks/deprecation.rb | 2 +- lib/rspec/mocks/framework.rb | 1 + lib/rspec/mocks/method_double.rb | 2 +- lib/rspec/mocks/proxy_for_nil.rb | 4 +- lib/rspec/mocks/syntax.rb | 6 +-- lib/rspec/mocks/test_double.rb | 2 +- 9 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 benchmarks/caller.rb create mode 100644 lib/rspec/mocks/caller_filter.rb diff --git a/benchmarks/caller.rb b/benchmarks/caller.rb new file mode 100644 index 000000000..bc8f7c0d3 --- /dev/null +++ b/benchmarks/caller.rb @@ -0,0 +1,81 @@ +$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) + +require 'benchmark' +require 'rspec/mocks' + +n = 10000 + +puts "#{n} times - ruby #{RUBY_VERSION}" +puts + +puts "* Using a chunked fetch is quicker than the old method of array-access." +Benchmark.bm(20) do |bm| + bm.report("CallerFilter") do + n.times do + RSpec::Mocks::CallerFilter.first_non_rspec_line + end + end + + bm.report("Direct caller access") do + n.times do + caller(1)[4] + end + end +end + +puts +puts "* Chunking fetches of caller adds a ~17% overhead." +Benchmark.bm(20) do |bm| + bm.report("Chunked") do + n.times do + caller(1, 2) + caller(3, 2) + caller(5, 2) + end + end + + bm.report("All at once") do + n.times do + caller(1, 6) + end + end +end + +puts +puts "* `caller` scales linearly with length parameter." +Benchmark.bm(20) do |bm| + (1..10).each do |x| + bm.report(x) do + n.times do + caller(1, x) + end + end + end +end + + +# > ruby benchmarks/caller.rb +# 10000 times - ruby 2.0.0 +# +# * Using a chunked fetch is quicker than the old method of array-access. +# user system total real +# CallerFilter 0.140000 0.010000 0.150000 ( 0.145381) +# Direct caller access 0.170000 0.000000 0.170000 ( 0.180610) +# +# * Chunking fetches of caller adds a ~17% overhead. +# user system total real +# Chunked 0.150000 0.000000 0.150000 ( 0.181162) +# All at once 0.130000 0.010000 0.140000 ( 0.138732) +# +# * `caller` scales linearly with length parameter. +# user system total real +# 1 0.030000 0.000000 0.030000 ( 0.035000) +# 2 0.050000 0.000000 0.050000 ( 0.059879) +# 3 0.080000 0.000000 0.080000 ( 0.098468) +# 4 0.090000 0.010000 0.100000 ( 0.097619) +# 5 0.110000 0.000000 0.110000 ( 0.126220) +# 6 0.130000 0.000000 0.130000 ( 0.136739) +# 7 0.150000 0.000000 0.150000 ( 0.159055) +# 8 0.160000 0.010000 0.170000 ( 0.172416) +# 9 0.180000 0.000000 0.180000 ( 0.203038) +# 10 0.200000 0.000000 0.200000 ( 0.210551) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 2591bbe75..0f98e68cc 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -43,7 +43,9 @@ def any_instance_recorder_for(klass) # x = 0 # RSpec::Mocks.allow_message(bar, :foo) { x += 1 } def allow_message(subject, message, opts={}, &block) - orig_caller = opts.fetch(:expected_from) { caller(1)[0] } + orig_caller = opts.fetch(:expected_from) { + CallerFilter.first_non_rspec_line + } ::RSpec::Mocks.proxy_for(subject). add_stub(orig_caller, message.to_sym, opts, &block) end @@ -60,7 +62,9 @@ def allow_message(subject, message, opts={}, &block) # RSpec::Mocks.expect_message(bar, :foo) # bar.foo def expect_message(subject, message, opts={}, &block) - orig_caller = opts.fetch(:expected_from) { caller(1)[0] } + orig_caller = opts.fetch(:expected_from) { + CallerFilter.first_non_rspec_line + } ::RSpec::Mocks.proxy_for(subject). add_message_expectation(orig_caller, message.to_sym, opts, &block) end diff --git a/lib/rspec/mocks/caller_filter.rb b/lib/rspec/mocks/caller_filter.rb new file mode 100644 index 000000000..10ef6633b --- /dev/null +++ b/lib/rspec/mocks/caller_filter.rb @@ -0,0 +1,58 @@ +module RSpec + module Mocks + + # Consistent implementation for "cleaning" the caller method to strip out + # non-rspec lines. This enables errors to be reported at the call site in + # the code using the library, which is far more useful than the particular + # internal method that raised an error. + class CallerFilter + + # This list is an unfortunate dependency on other RSpec core libraries. + # It would be nice if this was not needed. + RSPEC_LIBS = %w[ + core + mocks + expectations + matchers + rails + ] + + LIB_REGEX = %r{/lib/rspec/(#{RSPEC_LIBS.join('|')})(\.rb|/)} + + if RUBY_VERSION >= '2.0.0' + def self.first_non_rspec_line + # `caller` is an expensive method that scales linearly with the size of + # the stack. The performance hit for fetching it in chunks is small, + # and since the target line is probably near the top of the stack, the + # overall improvement of a chunked search like this is significant. + # + # See benchmarks/caller.rb for measurements. + + # Initial value here is mostly arbitrary, but is chosen to give good + # performance on the common case of creating a double. + increment = 5 + i = 1 + line = nil + + while !line + stack = caller(i, increment) + raise "No non-lib lines in stack" unless stack + + line = stack.find { |line| line !~ LIB_REGEX } + + i += increment + increment *= 2 # The choice of two here is arbitrary. + end + + line + end + else + # Earlier rubies do not support the two argument form of `caller`. This + # fallback is logically the same, but slower. + def self.first_non_rspec_line + caller.find { |line| line !~ LIB_REGEX } + end + end + end + end +end diff --git a/lib/rspec/mocks/deprecation.rb b/lib/rspec/mocks/deprecation.rb index 50ff01d3c..e56ec6425 100644 --- a/lib/rspec/mocks/deprecation.rb +++ b/lib/rspec/mocks/deprecation.rb @@ -7,7 +7,7 @@ module Deprecation def deprecate(deprecated, options={}) message = "DEPRECATION: #{deprecated} is deprecated." message << " Use #{options[:replacement]} instead." if options[:replacement] - message << " Called from #{caller(0)[2]}." + message << " Called from #{CallerFilter.first_non_rspec_line}." warn message end end diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 2e776824c..e098a015b 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -2,6 +2,7 @@ # supports wrapping rspec's mocking functionality without invading every # object in the system. +require 'rspec/mocks/caller_filter' require 'rspec/mocks/deprecation' require 'rspec/mocks/instance_method_stasher' require 'rspec/mocks/method_double' diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index a84f991bf..3f8e8e823 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -147,7 +147,7 @@ def original_method_from_superclass |used instead; however, it may not work correctly when executed due |to the fact that `self` will be #{@object.superclass}, not #{@object}. | - |Called from: #{caller[2]} + |Called from: #{CallerFilter.first_non_rspec_line} WARNING @object.superclass.method(@method_name) diff --git a/lib/rspec/mocks/proxy_for_nil.rb b/lib/rspec/mocks/proxy_for_nil.rb index 434e08390..075033e90 100644 --- a/lib/rspec/mocks/proxy_for_nil.rb +++ b/lib/rspec/mocks/proxy_for_nil.rb @@ -28,8 +28,8 @@ def add_stub(location, method_name, opts={}, &implementation) private def warn method_name - non_rspec_caller = caller.find { |line| !line.include?('lib/rspec/mocks') } - Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{non_rspec_caller}. Use allow_message_expectations_on_nil to disable warnings.") + source = CallerFilter.first_non_rspec_line + Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.") end end diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 25eadf73d..a2c2b5d2c 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -12,12 +12,12 @@ def self.enable_should(syntax_host = default_should_syntax_host) syntax_host.class_exec do def should_receive(message, opts={}, &block) - opts[:expected_from] ||= caller(1)[0] + opts[:expected_from] ||= CallerFilter.first_non_rspec_line ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block) end def should_not_receive(message, &block) - opts = {:expected_from => caller(1)[0]} + opts = {:expected_from => CallerFilter.first_non_rspec_line} ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block).never end @@ -25,7 +25,7 @@ def stub(message_or_hash, opts={}, &block) if ::Hash === message_or_hash message_or_hash.each {|message, value| stub(message).and_return value } else - opts[:expected_from] = caller(1)[0] + opts[:expected_from] = CallerFilter.first_non_rspec_line ::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block) end end diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 16391c9f3..1fb1a35c1 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -99,7 +99,7 @@ def assign_stubs(stubs) # Performance optimization so that `caller` is not called needlessly. return if stubs.empty? - expected_from = caller(1)[0] + expected_from = CallerFilter.first_non_rspec_line stubs.each_pair do |message, response| __mock_proxy.add_simple_stub(expected_from, message, response) From 13e9d11542a6b60c5dc7ffa4527c98bb255d0a1f Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 11 Aug 2013 19:34:42 -0700 Subject: [PATCH 0153/1179] Remove unused arguments from simple stub interface. Not having to run `caller` makes a big difference in performance. --- lib/rspec/mocks/method_double.rb | 2 +- lib/rspec/mocks/proxy.rb | 4 ++-- lib/rspec/mocks/test_double.rb | 7 +------ lib/rspec/mocks/verifying_proxy.rb | 2 +- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 3f8e8e823..9fa99e0eb 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -251,7 +251,7 @@ def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &imp # example method. # # @private - def add_simple_stub(error_generator, expectation_ordering, expected_from, method_name, response) + def add_simple_stub(method_name, response) define_proxy_method stub = SimpleMessageExpectation.new(method_name, response) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index ce2ccd3b9..e96ea3e30 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -97,8 +97,8 @@ def add_stub(location, method_name, opts={}, &implementation) method_double[method_name].add_stub @error_generator, @expectation_ordering, location, opts, &implementation end - def add_simple_stub(location, method_name, response) - method_double[method_name].add_simple_stub @error_generator, @expectation_ordering, location, method_name, response + def add_simple_stub(method_name, response) + method_double[method_name].add_simple_stub method_name, response end # @private diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 1fb1a35c1..bb10bc092 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -96,13 +96,8 @@ def method_missing(message, *args, &block) end def assign_stubs(stubs) - # Performance optimization so that `caller` is not called needlessly. - return if stubs.empty? - - expected_from = CallerFilter.first_non_rspec_line - stubs.each_pair do |message, response| - __mock_proxy.add_simple_stub(expected_from, message, response) + __mock_proxy.add_simple_stub(message, response) end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 2a43e32e0..4b884f5ac 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -31,7 +31,7 @@ def add_stub(location, method_name, opts={}, &implementation) super end - def add_simple_stub(_, method_name, *args) + def add_simple_stub(method_name, *args) ensure_implemented(method_name) super end From b31c35ab4830524adf9e9545df18ac8aa956aa5e Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 10 Aug 2013 09:13:19 -0700 Subject: [PATCH 0154/1179] Add benchmark script. --- benchmarks/double_creation.rb | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 benchmarks/double_creation.rb diff --git a/benchmarks/double_creation.rb b/benchmarks/double_creation.rb new file mode 100644 index 000000000..654937407 --- /dev/null +++ b/benchmarks/double_creation.rb @@ -0,0 +1,66 @@ +$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) + +require 'benchmark' +require 'rspec/mocks' + +n = 1000 + +puts "#{n} times - ruby #{RUBY_VERSION}" +puts + +Benchmark.bm do |bm| + RSpec::Mocks.setup(self) + + (0..9).each do |m| + attrs = m.times.inject({}) {|h, x| + h["method_#{x}"] = x + h + } + + bm.report("#{m} attrs") do + n.times do + double(attrs) + end + end + end +end +# $ export OLD_REV=d483e0a893d97c7b8e612e878a9f3562a210df9f +# $ git checkout $OLD_REV +# $ ruby benchmarks/double_creation.rb +# 1000 times - ruby 2.0.0 +# +# user system total real +# 0 attrs 0.010000 0.000000 0.010000 ( 0.003686) +# 1 attrs 0.110000 0.000000 0.110000 ( 0.143132) +# 2 attrs 0.230000 0.010000 0.240000 ( 0.311358) +# 3 attrs 0.400000 0.020000 0.420000 ( 0.465994) +# 4 attrs 0.570000 0.010000 0.580000 ( 0.597902) +# 5 attrs 0.920000 0.010000 0.930000 ( 1.060219) +# 6 attrs 1.350000 0.020000 1.370000 ( 1.388386) +# 7 attrs 1.770000 0.030000 1.800000 ( 1.805518) +# 8 attrs 2.620000 0.030000 2.650000 ( 2.681484) +# 9 attrs 3.320000 0.030000 3.350000 ( 3.380757) +# +# $ export NEW_REV=13e9d11542a6b60c5dc7ffa4527c98bb255d0a1f +# $ git checkout $NEW_REV +# $ ruby benchmarks/double_creation.rb +# 1000 times - ruby 2.0.0 +# +# user system total real +# 0 attrs 0.010000 0.000000 0.010000 ( 0.001544) +# 1 attrs 0.040000 0.000000 0.040000 ( 0.043522) +# 2 attrs 0.060000 0.000000 0.060000 ( 0.081742) +# 3 attrs 0.090000 0.010000 0.100000 ( 0.104526) +# 4 attrs 0.120000 0.010000 0.130000 ( 0.132472) +# 5 attrs 0.150000 0.010000 0.160000 ( 0.162368) +# 6 attrs 0.190000 0.010000 0.200000 ( 0.204610) +# 7 attrs 0.220000 0.010000 0.230000 ( 0.237983) +# 8 attrs 0.260000 0.010000 0.270000 ( 0.281562) +# 9 attrs 0.310000 0.020000 0.330000 ( 0.334489) +# +# $ git log $OLD_REV..$NEW_REV --oneline +# 13e9d11 Remove unused arguments from simple stub interface. +# 009a697 Extract CallerFilter class to unify caller manipulation. +# 46c1eb0 Introduce "simple" stub as an optimization over using a normal stub. +# 4a04b3e Extract constant ArgumentListMatcher::MATCH_ALL. +# 860d591 Speed up double creation with multiple attributes by caching caller. From 3b6142ab6dc0768d24c9bf1195fb858f896b246b Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 10 Aug 2013 09:04:45 -0700 Subject: [PATCH 0155/1179] Changelog entry covering all double performance enhancements. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 1595788bc..5b43873a0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -26,6 +26,8 @@ Enhancements: * Yield the receiver to `any_instance` implementation blocks (Sam Phippen). * Provide `instance_double` and `class_double` to create verifying doubles, ported from `rspec-fire` (Xavier Shay). +* Improved performance of double creation, particularly those with many + attributes. (Xavier Shay) ### 2.14.3 / 2013-08-08 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) From 0a64d664b4b230f46d17c53147dee64e729a5bc2 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Mon, 12 Aug 2013 10:48:53 -0300 Subject: [PATCH 0156/1179] Removing bug report. Doesn't fail when it should. --- spec/rspec/mocks/bug_report_10263_spec.rb | 27 ----------------------- 1 file changed, 27 deletions(-) delete mode 100644 spec/rspec/mocks/bug_report_10263_spec.rb diff --git a/spec/rspec/mocks/bug_report_10263_spec.rb b/spec/rspec/mocks/bug_report_10263_spec.rb deleted file mode 100644 index 5c1bdc580..000000000 --- a/spec/rspec/mocks/bug_report_10263_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' - -describe "Double" do - let(:test_double) { double } - - specify "when one example has an expectation inside the block passed to should_receive" do - test_double.should_receive(:msg) do |arg| - expect(arg).to be_truthy #this call exposes the problem - end - begin - test_double.msg(false) - rescue Exception - end - end - - specify "then the next example should behave as expected instead of saying" do - test_double.should_receive(:foobar) - test_double.foobar - verify test_double - begin - test_double.foobar - rescue Exception => e - expect(e.message).to eq "Double received unexpected message :foobar with (no args)" - end - end -end - From 8baa9b8644cd8dd50ad41467500670482e2d001d Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 12 Aug 2013 20:05:57 -0700 Subject: [PATCH 0157/1179] Benchmark of transfer_nested_constants option. Shows a non-trivial performance impact. --- benchmarks/transfer_nested_constants.rb | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 benchmarks/transfer_nested_constants.rb diff --git a/benchmarks/transfer_nested_constants.rb b/benchmarks/transfer_nested_constants.rb new file mode 100644 index 000000000..e46d42de3 --- /dev/null +++ b/benchmarks/transfer_nested_constants.rb @@ -0,0 +1,77 @@ +$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) + +require 'benchmark' +require 'rspec/mocks' + +N = ENV.fetch('N', 10000).to_i +M = ENV.fetch('M', 5).to_i + +puts "#{N} times, #{M} constants - ruby #{RUBY_VERSION}" +puts + +class A + M.times do |x| + const_set("C#{x}", Object.new) + end +end + +Benchmark.bm(20) do |bm| + RSpec::Mocks.setup(self) + + bm.report("with constants") do + N.times do + class_double('A').as_stubbed_const(:transfer_nested_constants => true) + end + end + + bm.report("without constants") do + N.times do + class_double('A').as_stubbed_const(:transfer_nested_constants => false) + end + end +end + +# > for n in 1 10000; do for m in 0 5 100; do echo; \ +# env N=$n M=$m ruby benchmarks/transfer_nested_constants.rb; \ +# echo; done; done +# +# 1 times, 0 constants - ruby 2.0.0 +# +# user system total real +# with constants 0.000000 0.000000 0.000000 ( 0.000180) +# without constants 0.000000 0.000000 0.000000 ( 0.000071) +# +# +# 1 times, 5 constants - ruby 2.0.0 +# +# user system total real +# with constants 0.000000 0.000000 0.000000 ( 0.000197) +# without constants 0.000000 0.000000 0.000000 ( 0.000123) +# +# +# 1 times, 100 constants - ruby 2.0.0 +# +# user system total real +# with constants 0.000000 0.000000 0.000000 ( 0.000433) +# without constants 0.000000 0.000000 0.000000 ( 0.000115) +# +# +# 10000 times, 0 constants - ruby 2.0.0 +# +# user system total real +# with constants 0.900000 0.020000 0.920000 ( 0.935583) +# without constants 0.660000 0.010000 0.670000 ( 0.680178) +# +# +# 10000 times, 5 constants - ruby 2.0.0 +# +# user system total real +# with constants 1.080000 0.020000 1.100000 ( 1.114722) +# without constants 0.720000 0.020000 0.740000 ( 0.741976) +# +# +# 10000 times, 100 constants - ruby 2.0.0 +# +# user system total real +# with constants 3.870000 0.110000 3.980000 ( 4.000176) +# without constants 0.930000 0.010000 0.940000 ( 0.947197) From 1e8bad8e82db8d1830b086b963a39d061da80a4d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 13 Aug 2013 23:08:43 -0700 Subject: [PATCH 0158/1179] Address warnings. rspec-mocks/lib/rspec/mocks/caller_filter.rb:41: warning: shadowing outer local variable - line rspec-mocks/spec/rspec/mocks/verifying_double_spec.rb:195: warning: assigned but unused variable - dbl2 --- lib/rspec/mocks/caller_filter.rb | 2 +- spec/rspec/mocks/verifying_double_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/caller_filter.rb b/lib/rspec/mocks/caller_filter.rb index 10ef6633b..00befbc5e 100644 --- a/lib/rspec/mocks/caller_filter.rb +++ b/lib/rspec/mocks/caller_filter.rb @@ -38,7 +38,7 @@ def self.first_non_rspec_line stack = caller(i, increment) raise "No non-lib lines in stack" unless stack - line = stack.find { |line| line !~ LIB_REGEX } + line = stack.find { |l| l !~ LIB_REGEX } i += increment increment *= 2 # The choice of two here is arbitrary. diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 6f36d2d5d..61f19bd3d 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -192,7 +192,7 @@ def verifying_constant_names(bool, &block) it 'correctly verifies expectations when constant is removed' do dbl1 = class_double(LoadedClass::Nested).as_stubbed_const - dbl2 = class_double(LoadedClass).as_stubbed_const + class_double(LoadedClass).as_stubbed_const prevents { expect(dbl1).to receive(:undefined_class_method) From c32ad6c22a426f6d783adf3919c7584784964c0e Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Wed, 14 Aug 2013 21:43:23 -0300 Subject: [PATCH 0159/1179] Moving inheritance specs to partial mock --- spec/rspec/mocks/bug_report_496_spec.rb | 17 ----------------- spec/rspec/mocks/bug_report_7611_spec.rb | 16 ---------------- spec/rspec/mocks/bug_report_957_spec.rb | 22 ---------------------- spec/rspec/mocks/partial_mock_spec.rb | 20 ++++++++++++++++++++ 4 files changed, 20 insertions(+), 55 deletions(-) delete mode 100644 spec/rspec/mocks/bug_report_496_spec.rb delete mode 100644 spec/rspec/mocks/bug_report_7611_spec.rb delete mode 100644 spec/rspec/mocks/bug_report_957_spec.rb diff --git a/spec/rspec/mocks/bug_report_496_spec.rb b/spec/rspec/mocks/bug_report_496_spec.rb deleted file mode 100644 index 285929cee..000000000 --- a/spec/rspec/mocks/bug_report_496_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -module BugReport496 - describe "a message expectation on a base class object" do - class BaseClass - end - - class SubClass < BaseClass - end - - it "is received" do - BaseClass.should_receive(:new).once - SubClass.new - end - end -end - diff --git a/spec/rspec/mocks/bug_report_7611_spec.rb b/spec/rspec/mocks/bug_report_7611_spec.rb deleted file mode 100644 index b4161a226..000000000 --- a/spec/rspec/mocks/bug_report_7611_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -module Bug7611 - describe "A Partial Mock" do - class Foo; end - class Bar < Foo; end - - it "respects subclasses" do - Foo.stub(:new).and_return(Object.new) - end - - it "should" do - expect(Bar.new.class).to eq Bar - end - end -end diff --git a/spec/rspec/mocks/bug_report_957_spec.rb b/spec/rspec/mocks/bug_report_957_spec.rb deleted file mode 100644 index c28838a32..000000000 --- a/spec/rspec/mocks/bug_report_957_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'spec_helper' - -module RSpec - module Mocks - describe "stubbing a base class class method" do - before do - @base_class = Class.new - @concrete_class = Class.new(@base_class) - - @base_class.stub(:find).and_return "stubbed_value" - end - - it "returns the value for the stub on the base class" do - expect(@base_class.find).to eq "stubbed_value" - end - - it "returns the value for the descendent class" do - expect(@concrete_class.find).to eq "stubbed_value" - end - end - end -end diff --git a/spec/rspec/mocks/partial_mock_spec.rb b/spec/rspec/mocks/partial_mock_spec.rb index acdeabf88..c00cc88a5 100644 --- a/spec/rspec/mocks/partial_mock_spec.rb +++ b/spec/rspec/mocks/partial_mock_spec.rb @@ -166,6 +166,26 @@ def ==(other) end end + describe 'Inheritance using partial mocks' do + + describe "stubbing a base class class method" do + before do + @base_class = Class.new + @concrete_class = Class.new(@base_class) + + @base_class.stub(:find).and_return "stubbed_value" + end + + it "returns the value for the stub on the base class" do + expect(@base_class.find).to eq "stubbed_value" + end + + it "returns the value for the descendent class" do + expect(@concrete_class.find).to eq "stubbed_value" + end + end + end + describe "Method visibility when using partial mocks" do let(:klass) do Class.new do From d230e18711cf4cecaba348ea1ceb1809c11ae6ea Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Wed, 14 Aug 2013 22:08:31 -0300 Subject: [PATCH 0160/1179] Moving bug report to expectation_error spec --- ...report_830_spec.rb => mock_expectation_error_spec.rb} | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) rename spec/rspec/mocks/{bug_report_830_spec.rb => mock_expectation_error_spec.rb} (51%) diff --git a/spec/rspec/mocks/bug_report_830_spec.rb b/spec/rspec/mocks/mock_expectation_error_spec.rb similarity index 51% rename from spec/rspec/mocks/bug_report_830_spec.rb rename to spec/rspec/mocks/mock_expectation_error_spec.rb index 7648201bc..6b19f440a 100644 --- a/spec/rspec/mocks/bug_report_830_spec.rb +++ b/spec/rspec/mocks/mock_expectation_error_spec.rb @@ -1,8 +1,11 @@ require 'spec_helper' +# Mock still fails on methods that catch StandardError AKA MockExpectationError +# does not inherit from StandardError module RSpec module Mocks - describe 'Calling a method that catches StandardError' do + describe 'MockExpectationError' do + class Foo def self.foo bar @@ -10,8 +13,8 @@ def self.foo end end - it 'still reports mock failures' do - Foo.should_not_receive :bar + it 'is not caught by StandardError rescue blocks' do + expect(Foo).not_to receive(:bar) expect { Foo.foo }.to raise_error(RSpec::Mocks::MockExpectationError) From 364ff032e8f9f8bc56fe44e17666fc04e6ef738a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 14 Aug 2013 18:11:53 -0700 Subject: [PATCH 0161/1179] Don't unnecessarily subclass Hash. --- .../mocks/any_instance/message_chains.rb | 33 ++++++++++++++----- lib/rspec/mocks/method_double.rb | 18 +++------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/lib/rspec/mocks/any_instance/message_chains.rb b/lib/rspec/mocks/any_instance/message_chains.rb index eb2391b3a..d99e72649 100644 --- a/lib/rspec/mocks/any_instance/message_chains.rb +++ b/lib/rspec/mocks/any_instance/message_chains.rb @@ -2,48 +2,63 @@ module RSpec module Mocks module AnyInstance # @private - class MessageChains < Hash + class MessageChains def initialize - super {|h,k| h[k] = []} + @chains_by_method_name = Hash.new { |h, k| h[k] = [] } + end + + # @private + def [](method_name) + @chains_by_method_name[method_name] end # @private def add(method_name, chain) - self[method_name] << chain + @chains_by_method_name[method_name] << chain chain end # @private def remove_stub_chains_for!(method_name) - self[method_name].reject! {|chain| chain.is_a?(StubChain)} + @chains_by_method_name[method_name].reject! do |chain| + chain.is_a?(StubChain) + end end # @private def has_expectation?(method_name) - self[method_name].find {|chain| chain.is_a?(ExpectationChain)} + @chains_by_method_name[method_name].find do |chain| + chain.is_a?(ExpectationChain) + end end # @private def all_expectations_fulfilled? - all? {|method_name, chains| chains.all? {|chain| chain.expectation_fulfilled?}} + @chains_by_method_name.all? do |method_name, chains| + chains.all? { |chain| chain.expectation_fulfilled? } + end end # @private def unfulfilled_expectations - map do |method_name, chains| + @chains_by_method_name.map do |method_name, chains| method_name.to_s if chains.last.is_a?(ExpectationChain) unless chains.last.expectation_fulfilled? end.compact end # @private def received_expected_message!(method_name) - self[method_name].each {|chain| chain.expectation_fulfilled!} + @chains_by_method_name[method_name].each do |chain| + chain.expectation_fulfilled! + end end # @private def playback!(instance, method_name) raise_if_second_instance_to_receive_message(instance) - self[method_name].each {|chain| chain.playback!(instance)} + @chains_by_method_name[method_name].each do |chain| + chain.playback!(instance) + end end private diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 9fa99e0eb..66787e4c7 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -1,9 +1,9 @@ module RSpec module Mocks # @private - class MethodDouble < Hash + class MethodDouble # @private - attr_reader :method_name, :object + attr_reader :method_name, :object, :expectations, :stubs # @private def initialize(object, method_name, proxy) @@ -13,18 +13,8 @@ def initialize(object, method_name, proxy) @method_stasher = InstanceMethodStasher.new(object_singleton_class, @method_name) @method_is_proxied = false - store(:expectations, []) - store(:stubs, []) - end - - # @private - def expectations - self[:expectations] - end - - # @private - def stubs - self[:stubs] + @expectations = [] + @stubs = [] end # @private From 0f2843d224bd2a1345fd8f616787c67e77c4df56 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Wed, 14 Aug 2013 22:29:03 -0300 Subject: [PATCH 0162/1179] Adding specs to prevent leakage and removing bug report 600 that tested for the same funcionality --- spec/rspec/mocks/bug_report_600_spec.rb | 22 ---------------------- spec/rspec/mocks/partial_mock_spec.rb | 13 ++++++++++++- 2 files changed, 12 insertions(+), 23 deletions(-) delete mode 100644 spec/rspec/mocks/bug_report_600_spec.rb diff --git a/spec/rspec/mocks/bug_report_600_spec.rb b/spec/rspec/mocks/bug_report_600_spec.rb deleted file mode 100644 index 78e3b279c..000000000 --- a/spec/rspec/mocks/bug_report_600_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'spec_helper' - -module BugReport600 - describe "stubbing a class method" do - class ExampleClass - def self.method_that_uses_define_method - define_method "defined_method" do |attributes| - load_address(address, attributes) - end - end - end - - it "works" do - ExampleClass.should_receive(:define_method).with("defined_method") - ExampleClass.method_that_uses_define_method - end - - it "restores the original method" do - ExampleClass.method_that_uses_define_method - end - end -end diff --git a/spec/rspec/mocks/partial_mock_spec.rb b/spec/rspec/mocks/partial_mock_spec.rb index c00cc88a5..cc85b640c 100644 --- a/spec/rspec/mocks/partial_mock_spec.rb +++ b/spec/rspec/mocks/partial_mock_spec.rb @@ -166,7 +166,18 @@ def ==(other) end end - describe 'Inheritance using partial mocks' do + describe "A partial class mock that has been subclassed" do + + it "cleans up stubs during #reset to prevent leakage onto subclasses between examples" do + klass = Class.new + subklass = Class.new(klass) + allow(klass).to receive(:new).and_return(:new_foo) + expect(subklass.new).to eq :new_foo + + reset(klass) + + expect(subklass.new).to be_a(subklass) + end describe "stubbing a base class class method" do before do From 847c66d55f2ec0ffc56aa27638a3469a03ddab3d Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 12 Aug 2013 19:39:33 -0700 Subject: [PATCH 0163/1179] Fix behaviour for simple stubs when stubbing core methods. --- lib/rspec/mocks/method_double.rb | 9 ++++++--- spec/rspec/mocks/double_spec.rb | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 9fa99e0eb..402a56bb4 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -183,8 +183,10 @@ def restore_original_method return unless @method_is_proxied object_singleton_class.__send__(:remove_method, @method_name) - @method_stasher.restore - restore_original_visibility + if @method_stasher.method_is_stashed? + @method_stasher.restore + restore_original_visibility + end @method_is_proxied = false end @@ -248,7 +250,8 @@ def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &imp # cannot match on arguments. It is used as an optimization over # `add_stub` where it is known in advance that this is all that will be # required of a stub, such as when passing attributes to the `double` - # example method. + # example method. They do not stash or restore existing method + # definitions. # # @private def add_simple_stub(method_name, response) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index c9d5ec1cc..3d834c677 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -9,4 +9,11 @@ double = double('name') expect {double.foo}.to raise_error(/Double "name" received/) end + + it 'restores standard object methods on reset' do + dbl = double(:tainted? => true) + expect(dbl.tainted?).to eq(true) + reset dbl + expect(dbl.tainted?).to eq(false) + end end From c305f6d024d802224b38a12ff7b8d4fda173b06a Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Fri, 29 Mar 2013 22:18:02 -0400 Subject: [PATCH 0164/1179] Illustrate bug with current ancestor approach --- spec/rspec/mocks/and_call_original_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 38f89e847..d89bf70c8 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -77,6 +77,15 @@ def instance.foo; :bar; end expect(sub_klass.foo).to eq(:sub_klass_bar) end + it "finds the method on the most direct singleton class ancestors even if the method " + + "is available on more distant ancestors" do + klass.extend Module.new { def foo; :klass_bar; end } + sub_klass = Class.new(klass) { def self.foo; :sub_klass_bar; end } + sub_sub_klass = Class.new(sub_klass) + sub_sub_klass.should_receive(:foo).and_call_original + expect(sub_sub_klass.foo).to eq(:sub_klass_bar) + end + context 'when using any_instance' do it 'works for instance methods defined on the class' do klass.any_instance.should_receive(:meth_1).and_call_original From c864e0a530add6638e404b84d1608652f3882060 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 1 Apr 2013 22:32:27 -0700 Subject: [PATCH 0165/1179] Candidate fix for #258. --- lib/rspec/mocks/method_double.rb | 124 +++------------------ spec/rspec/mocks/and_call_original_spec.rb | 41 +++---- 2 files changed, 32 insertions(+), 133 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index d6938ddd1..69d91fc05 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -3,7 +3,7 @@ module Mocks # @private class MethodDouble # @private - attr_reader :method_name, :object, :expectations, :stubs + attr_reader :method_name, :object, :expectations, :stubs, :original_method # @private def initialize(object, method_name, proxy) @@ -13,10 +13,26 @@ def initialize(object, method_name, proxy) @method_stasher = InstanceMethodStasher.new(object_singleton_class, @method_name) @method_is_proxied = false + @original_method = find_original_method @expectations = [] @stubs = [] end + def find_original_method + method_handle_name = if any_instance_class_recorder_observing_method?(@object.class) + recorder = ::RSpec::Mocks.any_instance_recorder_for(@object.class) + recorder.build_alias_method_name(@method_name) + else + @method_name + end + + ::RSpec::Mocks.method_handle_for(@object, method_handle_name) + rescue NameError + Proc.new do |*args, &block| + @object.__send__(:method_missing, @method_name, *args, &block) + end + end + # @private def visibility if TestDouble === @object @@ -30,46 +46,6 @@ def visibility end end - class ProcWithBlock < Struct.new(:object, :method_name) - - def call(*args, &block) - self.object.__send__(:method_missing, self.method_name, *args, &block) - end - - end - - # @private - def original_method - if @method_stasher.method_is_stashed? - # Example: a singleton method defined on @object - ::RSpec::Mocks.method_handle_for(@object, @method_stasher.stashed_method_name) - elsif meth = original_unrecorded_any_instance_method - # Example: a method that has been mocked through - # klass.any_instance.should_receive(:msg).and_call_original - # any_instance.should_receive(:msg) causes the method to be - # replaced with a proxy method, and then `and_call_original` - # is recorded and played back on the object instance. We need - # special handling here to get a handle on the original method - # object rather than the proxy method. - meth - else - # Example: an instance method defined on one of @object's ancestors. - original_method_from_ancestry - end - rescue NameError - # We have no way of knowing if the object's method_missing - # will handle this message or not...but we can at least try. - # If it's not handled, a `NoMethodError` will be raised, just - # like normally. - ProcWithBlock.new(@object,@method_name) - end - - def original_unrecorded_any_instance_method - return nil unless any_instance_class_recorder_observing_method?(@object.class) - alias_name = ::RSpec::Mocks.any_instance_recorder_for(@object.class).build_alias_method_name(@method_name) - @object.method(alias_name) - end - def any_instance_class_recorder_observing_method?(klass) return true if ::RSpec::Mocks.any_instance_recorder_for(klass).already_observing?(@method_name) superklass = klass.superclass @@ -77,72 +53,6 @@ def any_instance_class_recorder_observing_method?(klass) any_instance_class_recorder_observing_method?(superklass) end - our_singleton_class = class << self; self; end - if our_singleton_class.ancestors.include? our_singleton_class - # In Ruby 2.1, ancestors include the correct ancestors, including the singleton classes - def original_method_from_ancestry - # Lookup in the ancestry, skipping over the singleton class itself - original_method_from_ancestor(object_singleton_class.ancestors.drop(1)) - end - else - # @private - def original_method_from_ancestry - original_method_from_ancestor(object_singleton_class.ancestors) - rescue NameError - raise unless @object.respond_to?(:superclass) - - # Example: a singleton method defined on @object's superclass. - # - # Note: we have to give precedence to instance methods - # defined on @object's class, because in a case like: - # - # `klass.should_receive(:new).and_call_original` - # - # ...we want `Class#new` bound to `klass` (which will return - # an instance of `klass`), not `klass.superclass.new` (which - # would return an instance of `klass.superclass`). - original_method_from_superclass - end - end - - def original_method_from_ancestor(ancestors) - klass, *rest = ancestors - klass.instance_method(@method_name).bind(@object) - rescue NameError - raise if rest.empty? - original_method_from_ancestor(rest) - end - - if RUBY_VERSION.to_f > 1.8 - # @private - def original_method_from_superclass - @object.superclass. - singleton_class. - instance_method(@method_name). - bind(@object) - end - else - # Our implementation for 1.9 (above) causes an error on 1.8: - # TypeError: singleton method bound for a different object - # - # This doesn't work quite right in all circumstances but it's the - # best we can do. - # @private - def original_method_from_superclass - ::Kernel.warn <<-WARNING.gsub(/^ +\|/, '') - | - |WARNING: On ruby 1.8, rspec-mocks is unable to bind the original - |`#{@method_name}` method to your partial mock object (#{@object}) - |for `and_call_original`. The superclass's `#{@method_name}` is being - |used instead; however, it may not work correctly when executed due - |to the fact that `self` will be #{@object.superclass}, not #{@object}. - | - |Called from: #{CallerFilter.first_non_rspec_line} - WARNING - - @object.superclass.method(@method_name) - end - end # @private def object_singleton_class class << @object; self; end diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index d89bf70c8..0cced0784 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -26,6 +26,12 @@ def self.new_instance expect(instance.meth_1).to eq(:original) end + it 'ignores prior declared stubs' do + instance.stub(:meth_1).and_return(:stubbed_value) + instance.should_receive(:meth_1).and_call_original + expect(instance.meth_1).to eq(:original) + end + it 'passes args and blocks through to the original method' do instance.should_receive(:meth_2).and_call_original value = instance.meth_2(:submitted_arg) { |a, b| [a, b] } @@ -104,33 +110,16 @@ def instance.foo; :bar; end end end - if RUBY_VERSION.to_f > 1.8 - it 'works for class methods defined on a superclass' do - subclass = Class.new(klass) - subclass.should_receive(:new_instance).and_call_original - expect(subclass.new_instance).to be_a(subclass) - end - - it 'works for class methods defined on a grandparent class' do - sub_subclass = Class.new(Class.new(klass)) - sub_subclass.should_receive(:new_instance).and_call_original - expect(sub_subclass.new_instance).to be_a(sub_subclass) - end - else - it 'attempts to work for class methods defined on a superclass but ' + - 'executes the method with `self` as the superclass' do - ::Kernel.stub(:warn) - subclass = Class.new(klass) - subclass.should_receive(:new_instance).and_call_original - expect(subclass.new_instance).to be_an_instance_of(klass) - end + it 'works for class methods defined on a superclass' do + subclass = Class.new(klass) + subclass.should_receive(:new_instance).and_call_original + expect(subclass.new_instance).to be_a(subclass) + end - it 'prints a warning to notify users that `self` will not be correct' do - subclass = Class.new(klass) - ::Kernel.should_receive(:warn).with(/may not work correctly/) - subclass.should_receive(:new_instance).and_call_original - subclass.new_instance - end + it 'works for class methods defined on a grandparent class' do + sub_subclass = Class.new(Class.new(klass)) + sub_subclass.should_receive(:new_instance).and_call_original + expect(sub_subclass.new_instance).to be_a(sub_subclass) end it 'works for class methods defined on the Class class' do From 185b3a257f511ae9faaab1c8d42d9826cdd44f2e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 2 Jul 2013 10:33:57 -0700 Subject: [PATCH 0166/1179] Add a benchmark for finding the original method early. --- benchmarks/find_original_method_early.rb | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 benchmarks/find_original_method_early.rb diff --git a/benchmarks/find_original_method_early.rb b/benchmarks/find_original_method_early.rb new file mode 100644 index 000000000..596c6c218 --- /dev/null +++ b/benchmarks/find_original_method_early.rb @@ -0,0 +1,65 @@ +$LOAD_PATH.unshift "./lib" +require 'rspec/mocks' +require "rspec/mocks/standalone" + +=begin +This benchmark script is for troubleshooting the performance of +#264. To use it, you have to edit the code in #264 a bit: +wrap the call in `MethodDouble#initialize` to `find_original_method` +in a conditional like `if $find_original`. + +That allows the code below to compare the perf of stubbing a method +with the original method being found vs. not. +=end + +require 'benchmark' + +n = 10000 + +Foo = Class.new(Object) do + n.times do |i| + define_method "meth_#{i}" do + end + end +end + +Benchmark.bmbm do |bm| + puts "#{n} times - ruby #{RUBY_VERSION}" + + perform_report = lambda do |label, find_original, &create_object| + dbl = create_object.call + $find_original = find_original + + bm.report(label) do + n.times do |i| + dbl.stub("meth_#{i}") + end + end + + RSpec::Mocks.space.reset_all + end + + perform_report.call("Find original - partial mock", true) { Foo.new } + perform_report.call("Don't find original - partial mock", false) { Foo.new } + perform_report.call("Find original - test double", true) { double } + perform_report.call("Don't find original - test double", false) { double } +end + +=begin + +10000 times - ruby 1.9.3 +Rehearsal ---------------------------------------------------------------------- +Don't find original - partial mock 1.050000 0.020000 1.070000 ( 1.068561) +Don't find original - test double 1.190000 0.010000 1.200000 ( 1.199815) +Find original - partial mock 1.270000 0.010000 1.280000 ( 1.282944) +Find original - test double 1.320000 0.020000 1.340000 ( 1.336136) +------------------------------------------------------------- total: 4.890000sec + + user system total real +Don't find original - partial mock 0.990000 0.000000 0.990000 ( 1.000959) +Don't find original - test double 0.930000 0.010000 0.940000 ( 0.931871) +Find original - partial mock 1.040000 0.000000 1.040000 ( 1.046354) +Find original - test double 0.980000 0.010000 0.990000 ( 0.983577) + +=end + From 87d939ded8630646ac7be5a9bd42a0d3b16a514f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 4 Jul 2013 23:39:17 -0700 Subject: [PATCH 0167/1179] Refactor to improve perf. - Don't get the original method handle for a test double (it doesn't have an original method as its not a partial mock). - Leverage InstanceMethodStasher; since it has to stash the method anyway, it can store it so we don't have to waste effort getting it. - Delay getting the original_method until the last possible moment: right before we redefine it with a new definition. As a side benefit, this change makes it so that we no longer "leak" odd obfuscated aliased methods onto partial mock objects. This is a good thing. I ran benchmarks of this commits vs the prior (unoptimized fix) vs what we originally had in master using this shell command: time (for i in {1..20}; do bin/rspec -fp --order default; done) Here are the totals: - this commit: 39.070 seconds - prior unoptimized fix (c864e0a530add6638e404b84d1608652f3882060): 39.936 - master (80d80d919434e6fe0425b3fa5edab49fe28ea9a9): 37.783 --- lib/rspec/mocks/instance_method_stasher.rb | 30 ++++++++----------- lib/rspec/mocks/method_double.rb | 29 +++++------------- lib/rspec/mocks/proxy.rb | 28 +++++++++++++++++ lib/rspec/mocks/space.rb | 2 +- .../mocks/instance_method_stasher_spec.rb | 23 ++++++++++++-- 5 files changed, 70 insertions(+), 42 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index d3c220755..429b1f4f4 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -2,24 +2,25 @@ module RSpec module Mocks # @private class InstanceMethodStasher - def initialize(klass, method) - @klass = klass + attr_reader :original_method + + def initialize(object, method) + @object = object @method = method + @klass = (class << object; self; end) - @method_is_stashed = false + @original_method = nil end # @private def method_is_stashed? - @method_is_stashed + !!@original_method end # @private def stash - return if !method_defined_directly_on_klass? || @method_is_stashed - - @klass.__send__(:alias_method, stashed_method_name, @method) - @method_is_stashed = true + return if !method_defined_directly_on_klass? + @original_method ||= ::RSpec::Mocks.method_handle_for(@object, @method) end private @@ -61,21 +62,16 @@ def method_owned_by_klass? public - # @private - def stashed_method_name - "obfuscated_by_rspec_mocks__#{@method}" - end - # @private def restore - return unless @method_is_stashed + return unless @original_method if @klass.__send__(:method_defined?, @method) @klass.__send__(:undef_method, @method) end - @klass.__send__(:alias_method, @method, stashed_method_name) - @klass.__send__(:remove_method, stashed_method_name) - @method_is_stashed = false + + @klass.__send__(:define_method, @method, @original_method) + @original_method = nil end end end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 69d91fc05..c8f142ee6 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -3,7 +3,7 @@ module Mocks # @private class MethodDouble # @private - attr_reader :method_name, :object, :expectations, :stubs, :original_method + attr_reader :method_name, :object, :expectations, :stubs # @private def initialize(object, method_name, proxy) @@ -11,24 +11,14 @@ def initialize(object, method_name, proxy) @object = object @proxy = proxy - @method_stasher = InstanceMethodStasher.new(object_singleton_class, @method_name) + @method_stasher = InstanceMethodStasher.new(object, method_name) @method_is_proxied = false - @original_method = find_original_method @expectations = [] @stubs = [] end - def find_original_method - method_handle_name = if any_instance_class_recorder_observing_method?(@object.class) - recorder = ::RSpec::Mocks.any_instance_recorder_for(@object.class) - recorder.build_alias_method_name(@method_name) - else - @method_name - end - - ::RSpec::Mocks.method_handle_for(@object, method_handle_name) - rescue NameError - Proc.new do |*args, &block| + def original_method + @original_method ||= Proc.new do |*args, &block| @object.__send__(:method_missing, @method_name, *args, &block) end end @@ -46,13 +36,6 @@ def visibility end end - def any_instance_class_recorder_observing_method?(klass) - return true if ::RSpec::Mocks.any_instance_recorder_for(klass).already_observing?(@method_name) - superklass = klass.superclass - return false if superklass.nil? - any_instance_class_recorder_observing_method?(superklass) - end - # @private def object_singleton_class class << @object; self; end @@ -69,12 +52,16 @@ def configure_method def define_proxy_method return if @method_is_proxied + @original_method = @method_stasher.original_method || + @proxy.method_handle_for(method_name) + object_singleton_class.class_exec(method_name, visibility) do |method_name, visibility| define_method(method_name) do |*args, &block| ::RSpec::Mocks.proxy_for(self).message_received method_name, *args, &block end self.__send__ visibility, method_name end + @method_is_proxied = true end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index e96ea3e30..fe7c54cf4 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -28,6 +28,11 @@ def as_null_object @object end + # @private + def method_handle_for(message) + nil + end + # @private def already_proxied_respond_to @already_proxied_respond_to = true @@ -220,5 +225,28 @@ def find_almost_matching_stub(method_name, *args) method_double[method_name].stubs.find {|stub| stub.matches_name_but_not_args(method_name, *args)} end end + + class PartialMockProxy < Proxy + def method_handle_for(message) + if any_instance_class_recorder_observing_method?(@object.class, message) + message = ::RSpec::Mocks. + any_instance_recorder_for(@object.class). + build_alias_method_name(message) + end + + ::RSpec::Mocks.method_handle_for(@object, message) + rescue NameError + nil + end + + private + + def any_instance_class_recorder_observing_method?(klass, method_name) + return true if ::RSpec::Mocks.any_instance_recorder_for(klass).already_observing?(method_name) + superklass = klass.superclass + return false if superklass.nil? + any_instance_class_recorder_observing_method?(superklass, method_name) + end + end end end diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 20b1e7b61..77aeed4c2 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -53,7 +53,7 @@ def proxy_for(object) when NilClass then ProxyForNil.new when TestDouble then object.__build_mock_proxy else - Proxy.new(object) + PartialMockProxy.new(object) end end end diff --git a/spec/rspec/mocks/instance_method_stasher_spec.rb b/spec/rspec/mocks/instance_method_stasher_spec.rb index 30869f639..4baee6dcf 100644 --- a/spec/rspec/mocks/instance_method_stasher_spec.rb +++ b/spec/rspec/mocks/instance_method_stasher_spec.rb @@ -13,11 +13,15 @@ def singleton_class_for(obj) class << obj; self; end end + def stasher_for(obj, method_name) + InstanceMethodStasher.new(obj, method_name) + end + it "stashes the current implementation of an instance method so it can be temporarily replaced" do obj = Object.new def obj.hello; :hello_defined_on_singleton_class; end; - stashed_method = InstanceMethodStasher.new(singleton_class_for(obj), :hello) + stashed_method = stasher_for(obj, :hello) stashed_method.stash def obj.hello; :overridden_hello; end @@ -32,7 +36,7 @@ def obj.hello; :overridden_hello; end def obj.hello; :hello_defined_on_singleton_class; end; singleton_class_for(obj).__send__(:private, :hello) - stashed_method = InstanceMethodStasher.new(singleton_class_for(obj), :hello) + stashed_method = stasher_for(obj, :hello) stashed_method.stash def obj.hello; :overridden_hello; end @@ -43,7 +47,7 @@ def obj.hello; :overridden_hello; end it "only stashes methods directly defined on the given class, not its ancestors" do obj = ExampleClass.new - stashed_method = InstanceMethodStasher.new(singleton_class_for(obj), :hello) + stashed_method = stasher_for(obj, :hello) stashed_method.stash def obj.hello; :overridden_hello; end; @@ -52,6 +56,19 @@ def obj.hello; :overridden_hello; end; stashed_method.restore expect(obj.hello).to eql :overridden_hello end + + it "does not unnecessarily create obfuscated aliased methods" do + obj = Object.new + def obj.hello; :hello_defined_on_singleton_class; end; + + stashed_method = InstanceMethodStasher.new(singleton_class_for(obj), :hello) + + expect { + stashed_method.stash + }.not_to change { obj.methods.count } + + expect(obj.methods.grep(/rspec/)).to eq([]) + end end end end From 8aade6ef359593a5d6fe117ed0a914140e2359ce Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Thu, 15 Aug 2013 07:00:25 -0300 Subject: [PATCH 0168/1179] Removing unnecessary comments --- spec/rspec/mocks/mock_expectation_error_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/rspec/mocks/mock_expectation_error_spec.rb b/spec/rspec/mocks/mock_expectation_error_spec.rb index 6b19f440a..5c2c4ea67 100644 --- a/spec/rspec/mocks/mock_expectation_error_spec.rb +++ b/spec/rspec/mocks/mock_expectation_error_spec.rb @@ -1,7 +1,5 @@ require 'spec_helper' -# Mock still fails on methods that catch StandardError AKA MockExpectationError -# does not inherit from StandardError module RSpec module Mocks describe 'MockExpectationError' do From 7a11a548da3abe472918c087f8fef6c100750ca0 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Thu, 15 Aug 2013 07:09:29 -0300 Subject: [PATCH 0169/1179] Rewriting spec for partial mocking through inheritance --- spec/rspec/mocks/partial_mock_spec.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/spec/rspec/mocks/partial_mock_spec.rb b/spec/rspec/mocks/partial_mock_spec.rb index cc85b640c..28a6f91c5 100644 --- a/spec/rspec/mocks/partial_mock_spec.rb +++ b/spec/rspec/mocks/partial_mock_spec.rb @@ -168,9 +168,10 @@ def ==(other) describe "A partial class mock that has been subclassed" do + let(:klass) { Class.new } + let(:subklass) { Class.new(klass) } + it "cleans up stubs during #reset to prevent leakage onto subclasses between examples" do - klass = Class.new - subklass = Class.new(klass) allow(klass).to receive(:new).and_return(:new_foo) expect(subklass.new).to eq :new_foo @@ -181,18 +182,15 @@ def ==(other) describe "stubbing a base class class method" do before do - @base_class = Class.new - @concrete_class = Class.new(@base_class) - - @base_class.stub(:find).and_return "stubbed_value" + klass.stub(:find).and_return "stubbed_value" end it "returns the value for the stub on the base class" do - expect(@base_class.find).to eq "stubbed_value" + expect(klass.find).to eq "stubbed_value" end it "returns the value for the descendent class" do - expect(@concrete_class.find).to eq "stubbed_value" + expect(subklass.find).to eq "stubbed_value" end end end From 3ad5f4ab0a0ca52adfdc260d324db4e6512c166f Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 14 Aug 2013 20:22:53 -0700 Subject: [PATCH 0170/1179] Configuration option for default value for transfer_nested_constants. --- Changelog.md | 2 ++ lib/rspec/mocks/configuration.rb | 11 +++++++++++ lib/rspec/mocks/mutate_const.rb | 5 ++++- spec/rspec/mocks/mutate_const_spec.rb | 18 ++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 5b43873a0..5f6d95937 100644 --- a/Changelog.md +++ b/Changelog.md @@ -28,6 +28,8 @@ Enhancements: ported from `rspec-fire` (Xavier Shay). * Improved performance of double creation, particularly those with many attributes. (Xavier Shay) +* Default value of `transfer_nested_constants` option for constant stubbing can + be configured. (Xavier Shay) ### 2.14.3 / 2013-08-08 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 48dc82064..dc8feced3 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -6,6 +6,7 @@ class Configuration def initialize @yield_receiver_to_any_instance_implementation_blocks = true @verify_doubled_constant_names = false + @transfer_nested_constants = false end def yield_receiver_to_any_instance_implementation_blocks? @@ -70,6 +71,16 @@ def verify_doubled_constant_names? def verify_doubled_constant_names=(val) @verify_doubled_constant_names = val end + + def transfer_nested_constants? + !!@transfer_nested_constants + end + + # Sets the default for the `transfer_nested_constants` option when + # stubbing constants. + def transfer_nested_constants=(val) + @transfer_nested_constants = val + end end def self.configuration diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index cc0ecf5cb..698de3f3a 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -173,7 +173,10 @@ def self.stub(constant_name, value, options = {}) UndefinedConstantSetter end - mutate(mutator.new(constant_name, value, options[:transfer_nested_constants])) + mutate(mutator.new(constant_name, value, options.fetch( + :transfer_nested_constants, + RSpec::Mocks.configuration.transfer_nested_constants? + ))) value end diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 9f252b4a9..16fbd07f5 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -311,6 +311,24 @@ def change_const_value_to(value) expect(TestClass).to be(original_tc) end + + describe 'with global transfer_nested_constant option set' do + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.transfer_nested_constants = true + end + + it 'allows nested constants to be transferred to a stub module' do + tc_nested = TestClass::Nested + stub = Module.new + stub_const("TestClass", stub) + expect(stub::M).to eq(:m) + expect(stub::N).to eq(:n) + expect(stub::Nested).to be(tc_nested) + end + end + end context 'for a loaded nested constant' do From 26dbe69b0778119381156f9b36392a49b40032b3 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Thu, 15 Aug 2013 08:25:18 -0700 Subject: [PATCH 0171/1179] Use isolated configuration helper in verifying double specs. --- spec/rspec/mocks/verifying_double_spec.rb | 66 ++++++++++------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 6f36d2d5d..d8421bb5f 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -32,24 +32,12 @@ def prevents(&block) raise_error(RSpec::Mocks::MockExpectationError) end - def verifying_constant_names(bool, &block) - config = RSpec::Mocks.configuration - - original_value = config.verify_doubled_constant_names? - begin - config.verify_doubled_constant_names = bool - block.call - ensure - config.verify_doubled_constant_names = original_value - end - end - describe 'instance doubles' do describe 'when doubled class is not loaded' do - around do |example| - verifying_constant_names(false) do - example.run - end + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = false end it 'allows any instance method to be stubbed' do @@ -70,10 +58,10 @@ def verifying_constant_names(bool, &block) end describe 'when doubled class is loaded' do - around do |example| - verifying_constant_names(true) do - example.run - end + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = true end it 'only allows instance methods that exist to be stubbed' do @@ -124,10 +112,10 @@ def verifying_constant_names(bool, &block) describe 'class doubles' do describe 'when doubled class is not loaded' do - around do |example| - verifying_constant_names(false) do - example.run - end + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = false end it 'allows any method to be stubbed' do @@ -138,10 +126,10 @@ def verifying_constant_names(bool, &block) end describe 'when doubled class is loaded' do - around do |example| - verifying_constant_names(true) do - example.run - end + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = true end it 'only allows class methods that exist to be stubbed' do @@ -202,20 +190,22 @@ def verifying_constant_names(bool, &block) end describe 'when verify_doubled_constant_names config option is set' do + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = true + end + it 'prevents creation of instance doubles for unloaded constants' do - verifying_constant_names(true) do - expect { - instance_double('LoadedClas') - }.to raise_error(NameError) - end + expect { + instance_double('LoadedClas') + }.to raise_error(NameError) end it 'prevents creation of class doubles for unloaded constants' do - verifying_constant_names(true) do - expect { - class_double('LoadedClas') - }.to raise_error(NameError) - end + expect { + class_double('LoadedClas') + }.to raise_error(NameError) end end From 4d67b3f9cea3517445ac3aeebec0a0107dcd3703 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 15 Aug 2013 09:58:29 -0700 Subject: [PATCH 0172/1179] Fix for ruby 2.0. For some reason on Ruby 2.0, we were getting this error: TypeError: compared with non class/module # ./lib/rspec/mocks/instance_method_stasher.rb:73:in `define_method' # ./lib/rspec/mocks/instance_method_stasher.rb:73:in `restore' # ./lib/rspec/mocks/method_double.rb:74:in `restore_original_method' # ./lib/rspec/mocks/method_double.rb:94:in `reset' # ./lib/rspec/mocks/proxy.rb:123:in `block in reset' # ./lib/rspec/mocks/proxy.rb:123:in `each' # ./lib/rspec/mocks/proxy.rb:123:in `reset' # ./lib/rspec/mocks/proxy.rb:118:in `verify' # ./lib/rspec/mocks/space.rb:14:in `block in verify_all' # ./lib/rspec/mocks/space.rb:13:in `each_value' # ./lib/rspec/mocks/space.rb:13:in `verify_all' # ./lib/rspec/mocks.rb:18:in `verify' --- lib/rspec/mocks/instance_method_stasher.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index 429b1f4f4..cbe54772f 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -70,7 +70,7 @@ def restore @klass.__send__(:undef_method, @method) end - @klass.__send__(:define_method, @method, @original_method) + @klass.__send__(:define_method, @method, &@original_method) @original_method = nil end end From 6aa9bccef95856a94b8d56452939d860d99e39ba Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 15 Aug 2013 10:46:41 -0700 Subject: [PATCH 0173/1179] Add changelog entry for #264. [ci skip] --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 5b43873a0..abcacfd66 100644 --- a/Changelog.md +++ b/Changelog.md @@ -29,6 +29,11 @@ Enhancements: * Improved performance of double creation, particularly those with many attributes. (Xavier Shay) +Bug Fixes: + +* Fix `and_call_original` to handle a complex edge case involving + singleton class ancestors. (Marc-André Lafortune, Myron Marston) + ### 2.14.3 / 2013-08-08 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) From 9041c45c50fb0ce63b47d05e214e1aeeb579259d Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sat, 17 Aug 2013 16:02:13 +0100 Subject: [PATCH 0174/1179] Use the --strict flag on cucumber in script/test_all Signed-off-by: Sam Phippen --- script/test_all | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/test_all b/script/test_all index 729ef8cd7..cab05ad39 100755 --- a/script/test_all +++ b/script/test_all @@ -30,5 +30,4 @@ done # bundler, but just running `bin/cucumber` can fail due to the fact that it # shells out (via aruba) and executes `rspec`--which can pick up the wrong # rspec version if we're not running with bundler. -bundle exec cucumber - +bundle exec cucumber --strict From d8d0016c3c096918319538e9ebb77b3ea982f1e0 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Fri, 16 Aug 2013 21:04:35 +0000 Subject: [PATCH 0175/1179] Fixes typo in step name --- features/verifying_doubles/dynamic_classes.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index 1b30adf4b..11bdad99f 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -9,7 +9,7 @@ Feature: Dynamic classes be queried directly on the class. Background: - Given a file name "lib/fake_active_record.rb" + Given a file named "lib/fake_active_record.rb" with: """ruby class FakeActiveRecord COLUMNS = %w[name email] From 3aca6f5c096cdcab04bc73e7fba63334d3097194 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sat, 17 Aug 2013 15:46:06 +0000 Subject: [PATCH 0176/1179] Uses 1.8 hash syntax --- features/verifying_doubles/dynamic_classes.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index 11bdad99f..79a49f342 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -34,7 +34,7 @@ Feature: Dynamic classes describe User do it 'can be doubled' do - instance_double("User", name: "Don") + instance_double("User", :name => "Don") end end """ From 046dd46df2ff514cd0577c1e7da0a0ea327580fc Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sat, 17 Aug 2013 15:55:28 +0000 Subject: [PATCH 0177/1179] Removes use of `have` matchers which are removed in RSpec 3.0 --- spec/rspec/mocks/passing_argument_matchers_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/passing_argument_matchers_spec.rb b/spec/rspec/mocks/passing_argument_matchers_spec.rb index 9d399fa2a..fe70c05dd 100644 --- a/spec/rspec/mocks/passing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/passing_argument_matchers_spec.rb @@ -93,8 +93,8 @@ module Mocks it "matches arguments against RSpec expectations" do @double.should_receive(:random_call).with {|arg1, arg2, arr, *rest| expect(arg1).to eq 5 - expect(arg2).to have_at_least(3).characters - expect(arg2).to have_at_most(10).characters + expect(arg2.length).to be >= 3 + expect(arg2.length).to be <= 10 expect(arr.map {|i| i * 2}).to eq [2,4,6] expect(rest).to eq [:fee, "fi", 4] } From 78f81bfc12bf733f65d80e4f1db6f2fe2daacbb4 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sun, 18 Aug 2013 14:13:29 -0300 Subject: [PATCH 0178/1179] Removing old spec file. Testing some code that was long removed --- spec/rspec/mocks/bug_report_8165_spec.rb | 31 ------------------------ 1 file changed, 31 deletions(-) delete mode 100644 spec/rspec/mocks/bug_report_8165_spec.rb diff --git a/spec/rspec/mocks/bug_report_8165_spec.rb b/spec/rspec/mocks/bug_report_8165_spec.rb deleted file mode 100644 index e355789b3..000000000 --- a/spec/rspec/mocks/bug_report_8165_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'spec_helper' - -describe "An object where respond_to? is true and does not have method" do - # When should_receive(message) is sent to any object, the Proxy sends - # respond_to?(message) to that object to see if the method should be proxied. - # - # If respond_to? itself is proxied, then when the Proxy sends respond_to? - # to the object, the proxy is invoked and responds yes (if so set in the spec). - # When the object does NOT actually respond to `message`, an exception is thrown - # when trying to proxy it. - # - # The fix was to keep track of whether `respond_to?` had been proxied and, if - # so, call the munged copy of `respond_to?` on the object. - - it "does not raise an exception for Object" do - obj = Object.new - obj.should_receive(:respond_to?).with(:foobar).and_return(true) - obj.should_receive(:foobar).and_return(:baz) - expect(obj.respond_to?(:foobar)).to be_truthy - expect(obj.foobar).to eq :baz - end - - it "does not raise an exception for mock" do - obj = double("obj") - obj.should_receive(:respond_to?).with(:foobar).and_return(true) - obj.should_receive(:foobar).and_return(:baz) - expect(obj.respond_to?(:foobar)).to be_truthy - expect(obj.foobar).to eq :baz - end - -end From bd0d84f8151eeab1567bae0ad6f5cf37870e3e97 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Fri, 16 Aug 2013 01:02:11 +0000 Subject: [PATCH 0179/1179] Adds failing example demonstrating regression with #264 --- spec/rspec/mocks/stub_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index adb9d72f2..4174e959b 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -90,6 +90,25 @@ def existing_private_instance_method expect(@instance.existing_instance_method).to eq(:original_value) end + it "restores existing singleton methods with the appropriate context" do + klass = Class.new do + def self.say_hello + @hello if defined?(@hello) + end + end + + subclass = Class.new(klass) + + subclass.instance_variable_set(:@hello, "Hello") + expect(subclass.say_hello).to eq("Hello") + + klass.stub(:say_hello) { "Howdy" } + expect(subclass.say_hello).to eq("Howdy") + + reset klass + expect(subclass.say_hello).to eq("Hello") + end + it "restores existing private instance methods" do # See bug reports 8302 and 7805 @instance.stub(:existing_private_instance_method) { :stub_value } From 7a047e25cbed756bfe2724037aeafe393b738e5d Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Fri, 16 Aug 2013 20:54:27 +0000 Subject: [PATCH 0180/1179] Candidate fix for #264 * Calling `Method#to_proc` binds `self` to the object which does not do the right thing when accessing instance variables from a child class (see the test case) * Correctly restores the visibility of methods that we did not stash. The visibility might have changed, though, because the same method can have different visibilities depending on from where it is called. * Do not attempt to restore the visibility of simple stubs because their visibility will not change and we did not store it in the first place. --- lib/rspec/mocks/instance_method_stasher.rb | 4 ++-- lib/rspec/mocks/method_double.rb | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index cbe54772f..bc9a35537 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -41,7 +41,7 @@ def method_owned_by_klass? # On Ruby 2.0.0+ the owner of a method on a class which has been # `prepend`ed may actually be an instance, e.g. # `#`, rather than the expected `MyClass`. - owner = owner.class unless owner.is_a? Class + owner = owner.class unless owner.is_a? Module # On some 1.9s (e.g. rubinius) aliased methods # can report the wrong owner. Example: @@ -70,7 +70,7 @@ def restore @klass.__send__(:undef_method, @method) end - @klass.__send__(:define_method, @method, &@original_method) + @klass.__send__(:define_method, @method, @original_method) @original_method = nil end end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index c8f142ee6..5a5f98f3d 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -72,15 +72,18 @@ def restore_original_method object_singleton_class.__send__(:remove_method, @method_name) if @method_stasher.method_is_stashed? @method_stasher.restore - restore_original_visibility end + restore_original_visibility @method_is_proxied = false end # @private def restore_original_visibility - return unless object_singleton_class.method_defined?(@method_name) || object_singleton_class.private_method_defined?(@method_name) + return unless @original_visibility && + (object_singleton_class.method_defined?(@method_name) || + object_singleton_class.private_method_defined?(@method_name)) + object_singleton_class.__send__(*@original_visibility) end From 310b7831df36d18b1535f3ca7e7b5cabec507095 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 17 Aug 2013 23:56:45 -0700 Subject: [PATCH 0181/1179] Initialize ivar to avoid ruby warning. warning: instance variable @original_visibility not initialized --- lib/rspec/mocks/method_double.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 5a5f98f3d..b8556cab0 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -11,6 +11,7 @@ def initialize(object, method_name, proxy) @object = object @proxy = proxy + @original_visibility = nil @method_stasher = InstanceMethodStasher.new(object, method_name) @method_is_proxied = false @expectations = [] From bd8c08554eb3c26ddb157b6268d8fc41d1e41aa1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 21 Aug 2013 09:36:51 -0700 Subject: [PATCH 0182/1179] Fix spec that created the wrong stasher instance. --- spec/rspec/mocks/instance_method_stasher_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/instance_method_stasher_spec.rb b/spec/rspec/mocks/instance_method_stasher_spec.rb index 4baee6dcf..a3e20263a 100644 --- a/spec/rspec/mocks/instance_method_stasher_spec.rb +++ b/spec/rspec/mocks/instance_method_stasher_spec.rb @@ -61,7 +61,7 @@ def obj.hello; :overridden_hello; end; obj = Object.new def obj.hello; :hello_defined_on_singleton_class; end; - stashed_method = InstanceMethodStasher.new(singleton_class_for(obj), :hello) + stashed_method = stasher_for(obj, :hello) expect { stashed_method.stash From 02fc19b02c4e4b7095091df367872bf4c65049e5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 21 Aug 2013 09:14:08 -0700 Subject: [PATCH 0183/1179] Fix 1.8.7 failure by putting special-case code for 1.8.7. On 1.9+, the following code snippet works fine but it fails on 1.8.7 with `TypeError: singleton method bound for a different object` klass = Class.new do def self.say_hello; end end subclass = Class.new(klass) hello_method = klass.method(:say_hello) (class << klass; self; end).__send__(:define_method, :say_hello, &hello_method) subclass.say_hello The best we can do is to revert to the old stashing/unstashing code for 1.8.7. --- lib/rspec/mocks/instance_method_stasher.rb | 81 +++++++++++++------ .../mocks/instance_method_stasher_spec.rb | 2 +- 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index bc9a35537..d807d5b19 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -2,25 +2,72 @@ module RSpec module Mocks # @private class InstanceMethodStasher - attr_reader :original_method - def initialize(object, method) @object = object @method = method @klass = (class << object; self; end) @original_method = nil + @method_is_stashed = false end - # @private - def method_is_stashed? - !!@original_method - end + attr_reader :original_method - # @private - def stash - return if !method_defined_directly_on_klass? - @original_method ||= ::RSpec::Mocks.method_handle_for(@object, @method) + if RUBY_VERSION.to_f < 1.9 + # @private + def method_is_stashed? + @method_is_stashed + end + + # @private + def stash + return if !method_defined_directly_on_klass? || @method_is_stashed + + @klass.__send__(:alias_method, stashed_method_name, @method) + @method_is_stashed = true + end + + # @private + def stashed_method_name + "obfuscated_by_rspec_mocks__#{@method}" + end + private :stashed_method_name + + # @private + def restore + return unless @method_is_stashed + + if @klass.__send__(:method_defined?, @method) + @klass.__send__(:undef_method, @method) + end + @klass.__send__(:alias_method, @method, stashed_method_name) + @klass.__send__(:remove_method, stashed_method_name) + @method_is_stashed = false + end + else + + # @private + def method_is_stashed? + !!@original_method + end + + # @private + def stash + return if !method_defined_directly_on_klass? + @original_method ||= ::RSpec::Mocks.method_handle_for(@object, @method) + end + + # @private + def restore + return unless @original_method + + if @klass.__send__(:method_defined?, @method) + @klass.__send__(:undef_method, @method) + end + + @klass.__send__(:define_method, @method, @original_method) + @original_method = nil + end end private @@ -59,20 +106,6 @@ def method_owned_by_klass? # that the method is actually owned by @klass. owner == @klass || !(method_defined_on_klass?(owner)) end - - public - - # @private - def restore - return unless @original_method - - if @klass.__send__(:method_defined?, @method) - @klass.__send__(:undef_method, @method) - end - - @klass.__send__(:define_method, @method, @original_method) - @original_method = nil - end end end end diff --git a/spec/rspec/mocks/instance_method_stasher_spec.rb b/spec/rspec/mocks/instance_method_stasher_spec.rb index a3e20263a..8972f31d3 100644 --- a/spec/rspec/mocks/instance_method_stasher_spec.rb +++ b/spec/rspec/mocks/instance_method_stasher_spec.rb @@ -57,7 +57,7 @@ def obj.hello; :overridden_hello; end; expect(obj.hello).to eql :overridden_hello end - it "does not unnecessarily create obfuscated aliased methods" do + it "does not unnecessarily create obfuscated aliased methods", :if => (RUBY_VERSION.to_f > 1.8) do obj = Object.new def obj.hello; :hello_defined_on_singleton_class; end; From ea119b04cae53368b6ee3d2f6296997c85160c52 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 21 Aug 2013 09:48:17 -0700 Subject: [PATCH 0184/1179] Favor SomeClass === object over object.is_a?(SomeClass). It seems more likely for users to mess with `is_a?` on an individual object then `Class#===`, so it's better to use the latter. --- lib/rspec/mocks/any_instance/message_chains.rb | 10 +++++----- lib/rspec/mocks/any_instance/recorder.rb | 2 +- lib/rspec/mocks/argument_list_matcher.rb | 2 +- lib/rspec/mocks/argument_matchers.rb | 2 +- lib/rspec/mocks/error_generator.rb | 13 ++++++++----- lib/rspec/mocks/instance_method_stasher.rb | 2 +- lib/rspec/mocks/message_expectation.rb | 2 +- lib/rspec/mocks/mutate_const.rb | 4 ++-- lib/rspec/mocks/proxy.rb | 2 +- lib/rspec/mocks/test_double.rb | 2 +- 10 files changed, 22 insertions(+), 19 deletions(-) diff --git a/lib/rspec/mocks/any_instance/message_chains.rb b/lib/rspec/mocks/any_instance/message_chains.rb index d99e72649..a154b5ea9 100644 --- a/lib/rspec/mocks/any_instance/message_chains.rb +++ b/lib/rspec/mocks/any_instance/message_chains.rb @@ -21,14 +21,14 @@ def add(method_name, chain) # @private def remove_stub_chains_for!(method_name) @chains_by_method_name[method_name].reject! do |chain| - chain.is_a?(StubChain) + StubChain === chain end end # @private def has_expectation?(method_name) @chains_by_method_name[method_name].find do |chain| - chain.is_a?(ExpectationChain) + ExpectationChain === chain end end @@ -42,7 +42,7 @@ def all_expectations_fulfilled? # @private def unfulfilled_expectations @chains_by_method_name.map do |method_name, chains| - method_name.to_s if chains.last.is_a?(ExpectationChain) unless chains.last.expectation_fulfilled? + method_name.to_s if ExpectationChain === chains.last unless chains.last.expectation_fulfilled? end.compact end @@ -64,8 +64,8 @@ def playback!(instance, method_name) private def raise_if_second_instance_to_receive_message(instance) - @instance_with_expectation ||= instance if instance.is_a?(ExpectationChain) - if instance.is_a?(ExpectationChain) && !@instance_with_expectation.equal?(instance) + @instance_with_expectation ||= instance if ExpectationChain === instance + if ExpectationChain === instance && !@instance_with_expectation.equal?(instance) raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" end end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index cd4730ae2..3252631b1 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -26,7 +26,7 @@ def initialize(klass) # # @see Methods#stub def stub(method_name_or_method_map, &block) - if method_name_or_method_map.is_a?(Hash) + if Hash === method_name_or_method_map method_name_or_method_map.each do |method_name, return_value| stub(method_name).and_return(return_value) end diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index d9e6bf421..6959994a1 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -71,7 +71,7 @@ def args_match?(*args) def matcher_for(arg) return ArgumentMatchers::MatcherMatcher.new(arg) if is_matcher?(arg) - return ArgumentMatchers::RegexpMatcher.new(arg) if arg.is_a?(Regexp) + return ArgumentMatchers::RegexpMatcher.new(arg) if Regexp === arg return ArgumentMatchers::EqualityProxy.new(arg) end diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 6e69306ec..cbd372319 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -214,7 +214,7 @@ def hash_including(*args) # object.should_receive(:message).with(array_including(1,2,3)) # object.should_receive(:message).with(array_including([1,2,3])) def array_including(*args) - actually_an_array = args.first.is_a?(Array) && args.count == 1 ? args.first : args + actually_an_array = Array === args.first && args.count == 1 ? args.first : args ArrayIncludingMatcher.new(actually_an_array) end diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 605e5cbbb..b822f77e9 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -77,11 +77,14 @@ def expected_part_of_expectation_error(expected_received_count, expectation_coun # @private def method_call_args_description(args) - if args.first.is_a?(ArgumentMatchers::AnyArgsMatcher) - " with any arguments" - elsif args.first.is_a?(ArgumentMatchers::NoArgsMatcher) - " with no arguments" - elsif args.length > 0 + case args.first + when ArgumentMatchers::AnyArgsMatcher + return " with any arguments" + when ArgumentMatchers::NoArgsMatcher + return " with no arguments" + end + + if args.length > 0 " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}" else "" diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index d807d5b19..58d5cb9e6 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -88,7 +88,7 @@ def method_owned_by_klass? # On Ruby 2.0.0+ the owner of a method on a class which has been # `prepend`ed may actually be an instance, e.g. # `#`, rather than the expected `MyClass`. - owner = owner.class unless owner.is_a? Module + owner = owner.class unless Module === owner # On some 1.9s (e.g. rubinius) aliased methods # can report the wrong owner. Example: diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 27cb53282..893ad737e 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -127,7 +127,7 @@ def yield_receiver_to_implementation_block? # counter.increment # expect(counter.count).to eq(original_count + 1) def and_call_original - if @method_double.object.is_a?(RSpec::Mocks::TestDouble) + if RSpec::Mocks::TestDouble === @method_double.object @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original) else @implementation = AndCallOriginalImplementation.new(@method_double.original_method) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index 698de3f3a..d79ab15f7 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -64,7 +64,7 @@ def recursive_const_get(const_name) def recursive_const_defined?(const_name) normalize_const_name(const_name).split('::').inject([Object, '']) do |(mod, full_name), name| - yield(full_name, name) if block_given? && !mod.is_a?(Module) + yield(full_name, name) if block_given? && !(Module === mod) return false unless const_defined_on?(mod, name) [get_const_defined_on(mod, name), [mod, name].join('::')] end @@ -291,7 +291,7 @@ def verify_constants_to_transfer! end end - if @transfer_nested_constants.is_a?(Array) + if Array === @transfer_nested_constants @transfer_nested_constants = @transfer_nested_constants.map(&:to_s) if RUBY_VERSION == '1.8.7' undefined_constants = @transfer_nested_constants - constants_defined_on(@original_value) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index fe7c54cf4..247ac3d1f 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -159,7 +159,7 @@ def message_received(message, *args, &block) elsif stub = find_almost_matching_stub(message, *args) stub.advise(*args) raise_missing_default_stub_error(stub, *args) - elsif @object.is_a?(Class) + elsif Class === @object @object.superclass.__send__(message, *args, &block) else @object.__send__(:method_missing, message, *args, &block) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index bb10bc092..17173350d 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -68,7 +68,7 @@ def __build_mock_proxy private def __initialize_as_test_double(name=nil, stubs={}) - if name.is_a?(Hash) && stubs.empty? + if Hash === name && stubs.empty? stubs = name @name = nil else From 4c8afba039e9164ca492d91d2040814322769b43 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 21 Aug 2013 22:44:13 -0700 Subject: [PATCH 0185/1179] Address ruby warning. warning: mismatched indentations at 'end' with 'case' at 80 --- lib/rspec/mocks/error_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index b822f77e9..d325017fd 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -82,7 +82,7 @@ def method_call_args_description(args) return " with any arguments" when ArgumentMatchers::NoArgsMatcher return " with no arguments" - end + end if args.length > 0 " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}" From a988d3d820077c74c31845a3253b30cd67f9125c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 21 Aug 2013 22:45:33 -0700 Subject: [PATCH 0186/1179] Remove usage of deprecated config option. --- spec/spec_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3768bb797..7b610be10 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -35,7 +35,6 @@ def reset(object) config.color_enabled = true config.order = :random config.run_all_when_everything_filtered = true - config.treat_symbols_as_metadata_keys_with_true_values = true config.filter_run_including :focus config.expect_with :rspec do |expectations| From f585362c37b3c449cb7e54b09cd942baa6d99700 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 10 Aug 2013 02:33:31 +0100 Subject: [PATCH 0187/1179] Fix unstubing existing instances of any instance * access proxies for any instance of klass * remove stubs from "running" instances * pass recorder into chains to register stubs * reduce lookup cost of proxies by klass * record stubs used * remove just the stubs recorded by any instance * switch from for..in to each * allow unstubing of instances that are a sub class * refactor lookup for subclass * Improve doc string. * "local instance" didn't really give me the right * sense for what this was testing. * Improve space spec. - The old spec only checked the number of proxies returned, and didn't actually check that it returned the right ones. - The old spec only tested who were instances of the given class, and did not check instances of subclasses. --- Changelog.md | 2 ++ lib/rspec/mocks.rb | 4 +++ lib/rspec/mocks/any_instance/chain.rb | 3 +- lib/rspec/mocks/any_instance/recorder.rb | 13 +++++--- lib/rspec/mocks/any_instance/stub_chain.rb | 1 + lib/rspec/mocks/method_double.rb | 6 ++++ lib/rspec/mocks/proxy.rb | 9 ++++++ lib/rspec/mocks/space.rb | 8 +++-- .../mocks/any_instance/message_chains_spec.rb | 9 +++--- spec/rspec/mocks/any_instance_spec.rb | 24 ++++++++++++++ spec/rspec/mocks/space_spec.rb | 32 +++++++++++++++++++ 11 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 spec/rspec/mocks/space_spec.rb diff --git a/Changelog.md b/Changelog.md index 40490e9c6..164a5fbd4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -35,6 +35,8 @@ Bug Fixes: * Fix `and_call_original` to handle a complex edge case involving singleton class ancestors. (Marc-André Lafortune, Myron Marston) +* Fix issue where unstubing methods on "any instances" would not + remove stubs on existing instances (Jon Rowe) ### 2.14.3 / 2013-08-08 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 0f98e68cc..03eb0cb4e 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -26,6 +26,10 @@ def proxy_for(object) space.proxy_for(object) end + def proxies_of(klass) + space.proxies_of(klass) + end + def any_instance_recorder_for(klass) space.any_instance_recorder_for(klass) end diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 11c6bc61e..16c3350d2 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -2,7 +2,8 @@ module RSpec module Mocks module AnyInstance class Chain - def initialize(*args, &block) + def initialize(recorder, *args, &block) + @recorder = recorder @expectation_args = args @expectation_block = block end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index cd4730ae2..69b9ccd90 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -11,10 +11,11 @@ module AnyInstance # @see Chain class Recorder # @private - attr_reader :message_chains + attr_reader :message_chains, :stubs def initialize(klass) @message_chains = MessageChains.new + @stubs = Hash.new { |hash,key| hash[key] = [] } @observed_methods = [] @played_methods = {} @klass = klass @@ -32,7 +33,7 @@ def stub(method_name_or_method_map, &block) end else observe!(method_name_or_method_map) - message_chains.add(method_name_or_method_map, StubChain.new(method_name_or_method_map, &block)) + message_chains.add(method_name_or_method_map, StubChain.new(self, method_name_or_method_map, &block)) end end @@ -44,7 +45,7 @@ def stub(method_name_or_method_map, &block) def stub_chain(*method_names_and_optional_return_values, &block) normalize_chain(*method_names_and_optional_return_values) do |method_name, args| observe!(method_name) - message_chains.add(method_name, StubChainChain.new(*args, &block)) + message_chains.add(method_name, StubChainChain.new(self, *args, &block)) end end @@ -56,7 +57,7 @@ def stub_chain(*method_names_and_optional_return_values, &block) def should_receive(method_name, &block) @expectation_set = true observe!(method_name) - message_chains.add(method_name, PositiveExpectationChain.new(method_name, &block)) + message_chains.add(method_name, PositiveExpectationChain.new(self, method_name, &block)) end def should_not_receive(method_name, &block) @@ -72,6 +73,10 @@ def unstub(method_name) raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" end message_chains.remove_stub_chains_for!(method_name) + ::RSpec::Mocks.proxies_of(@klass).each do |proxy| + stubs[method_name].each { |stub| proxy.remove_single_stub(method_name, stub) } + end + stubs[method_name].clear stop_observing!(method_name) unless message_chains.has_expectation?(method_name) end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index 735058ed9..ef84a4937 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -15,6 +15,7 @@ def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.proxy_for(instance) expected_from = IGNORED_BACKTRACE_LINE stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block) + @recorder.stubs[stub.message] << stub if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? stub.and_yield_receiver_to_implementation diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index c8f142ee6..2891ae230 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -161,6 +161,12 @@ def remove_stub expectations.empty? ? reset : stubs.clear end + # @private + def remove_single_stub(stub) + stubs.delete(stub) + restore_original_method if stubs.empty? && expectations.empty? + end + # @private def raise_method_not_stubbed_error raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index fe7c54cf4..45e326089 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -15,6 +15,9 @@ def initialize(object, name=nil, options={}) @null_object = false end + # @private + attr_reader :object + # @private def null_object? @null_object @@ -102,6 +105,7 @@ def add_stub(location, method_name, opts={}, &implementation) method_double[method_name].add_stub @error_generator, @expectation_ordering, location, opts, &implementation end + # @private def add_simple_stub(method_name, response) method_double[method_name].add_simple_stub method_name, response end @@ -111,6 +115,11 @@ def remove_stub(method_name) method_double[method_name].remove_stub end + # @private + def remove_single_stub(method_name, stub) + method_double[method_name].remove_single_stub(stub) + end + # @private def verify method_doubles.each {|d| d.verify} diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 77aeed4c2..6e8ec55b2 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -5,8 +5,8 @@ class Space attr_reader :proxies, :any_instance_recorders def initialize - @proxies = {} - @any_instance_recorders = {} + @proxies = {} + @any_instance_recorders = {} end def verify_all @@ -46,6 +46,10 @@ def remove_any_instance_recorder_for(klass) any_instance_recorders.delete(klass.__id__) end + def proxies_of(klass) + proxies.values.select { |proxy| klass === proxy.object } + end + def proxy_for(object) id = id_for(object) proxies.fetch(id) do diff --git a/spec/rspec/mocks/any_instance/message_chains_spec.rb b/spec/rspec/mocks/any_instance/message_chains_spec.rb index 5df116007..fce8c7100 100644 --- a/spec/rspec/mocks/any_instance/message_chains_spec.rb +++ b/spec/rspec/mocks/any_instance/message_chains_spec.rb @@ -1,9 +1,10 @@ require 'spec_helper' describe RSpec::Mocks::AnyInstance::MessageChains do + let(:recorder) { double } let(:chains) { RSpec::Mocks::AnyInstance::MessageChains.new } - let(:stub_chain) { RSpec::Mocks::AnyInstance::StubChain.new } - let(:expectation_chain) { RSpec::Mocks::AnyInstance::PositiveExpectationChain.new } + let(:stub_chain) { RSpec::Mocks::AnyInstance::StubChain.new recorder } + let(:expectation_chain) { RSpec::Mocks::AnyInstance::PositiveExpectationChain.new recorder } it "knows if a method does not have an expectation set on it" do chains.add(:method_name, stub_chain) @@ -19,7 +20,7 @@ it "can remove all stub chains" do chains.add(:method_name, stub_chain) chains.add(:method_name, expectation_chain) - chains.add(:method_name, RSpec::Mocks::AnyInstance::StubChain.new) + chains.add(:method_name, RSpec::Mocks::AnyInstance::StubChain.new(recorder)) chains.remove_stub_chains_for!(:method_name) expect(chains[:method_name]).to eq([expectation_chain]) @@ -33,7 +34,7 @@ it "allows multiple stub chains for a method" do chains.add(:method_name, stub_chain) - chains.add(:method_name, another_stub_chain = RSpec::Mocks::AnyInstance::StubChain.new) + chains.add(:method_name, another_stub_chain = RSpec::Mocks::AnyInstance::StubChain.new(recorder)) expect(chains[:method_name]).to eq([stub_chain, another_stub_chain]) end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 123cd050e..c298dead5 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -279,6 +279,30 @@ class RSpec::SampleRspecTestClass;end expect(klass.new.existing_method).to eq(:existing_method_return_value) end + it "removes stubs even if they have already been invoked" do + klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + obj = klass.new + obj.existing_method + klass.any_instance.unstub(:existing_method) + expect(obj.existing_method).to eq(:existing_method_return_value) + end + + it "removes stubs from sub class after invokation when super class was originally stubbed" do + klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + obj = Class.new(klass).new + expect(obj.existing_method).to eq(:any_instance_value) + klass.any_instance.unstub(:existing_method) + expect(obj.existing_method).to eq(:existing_method_return_value) + end + + it "does not remove any stubs set directly on an instance" do + klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + obj = klass.new + obj.stub(:existing_method).and_return(:local_method) + klass.any_instance.unstub(:existing_method) + expect(obj.existing_method).to eq(:local_method) + end + it "does not remove any expectations with the same method name" do klass.any_instance.should_receive(:existing_method_with_arguments).with(3).and_return(:three) klass.any_instance.stub(:existing_method_with_arguments).with(1) diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb new file mode 100644 index 000000000..774416217 --- /dev/null +++ b/spec/rspec/mocks/space_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +module RSpec::Mocks + describe Space do + + describe "#proxies_of(klass)" do + let(:space) { Space.new } + + it 'returns proxies' do + space.proxy_for("") + expect(space.proxies_of(String).map(&:class)).to eq([PartialMockProxy]) + end + + it 'returns only the proxies whose object is an instance of the given class' do + grandparent_class = Class.new + parent_class = Class.new(grandparent_class) + child_class = Class.new(parent_class) + + grandparent = grandparent_class.new + parent = parent_class.new + child = child_class.new + + grandparent_proxy = space.proxy_for(grandparent) + parent_proxy = space.proxy_for(parent) + child_proxy = space.proxy_for(child) + + expect(space.proxies_of(parent_class)).to match_array([parent_proxy, child_proxy]) + end + end + + end +end From cd8ee94cafeafddda3692d59f256eaf8e4847aa9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 22 Aug 2013 23:55:42 -0700 Subject: [PATCH 0188/1179] Move caller_filter to `rspec/caller_filter`. - The caller_filter is generic for all of RSpec, not just rspec-mocks specific. - It's also at `rspec/caller_filter` in core (and will be in rspec-expectations soon). The idea is that any lib can require `rspec/caller_filter` and get this. - Add spec to verify the file stays up to date. --- lib/rspec/caller_filter.rb | 55 ++++++++++++++++++++++++++++++ lib/rspec/mocks/caller_filter.rb | 58 -------------------------------- lib/rspec/mocks/framework.rb | 2 +- spec/rspec/mocks_spec.rb | 13 +++++++ 4 files changed, 69 insertions(+), 59 deletions(-) create mode 100644 lib/rspec/caller_filter.rb delete mode 100644 lib/rspec/mocks/caller_filter.rb diff --git a/lib/rspec/caller_filter.rb b/lib/rspec/caller_filter.rb new file mode 100644 index 000000000..4e2dbe22f --- /dev/null +++ b/lib/rspec/caller_filter.rb @@ -0,0 +1,55 @@ +module RSpec + # Consistent implementation for "cleaning" the caller method to strip out + # non-rspec lines. This enables errors to be reported at the call site in + # the code using the library, which is far more useful than the particular + # internal method that raised an error. + class CallerFilter + + # This list is an unfortunate dependency on other RSpec core libraries. + # It would be nice if this was not needed. + RSPEC_LIBS = %w[ + core + mocks + expectations + matchers + rails + ] + + LIB_REGEX = %r{/lib/rspec/(#{RSPEC_LIBS.join('|')})(\.rb|/)} + + if RUBY_VERSION >= '2.0.0' + def self.first_non_rspec_line + # `caller` is an expensive method that scales linearly with the size of + # the stack. The performance hit for fetching it in chunks is small, + # and since the target line is probably near the top of the stack, the + # overall improvement of a chunked search like this is significant. + # + # See benchmarks/caller.rb for measurements. + + # Initial value here is mostly arbitrary, but is chosen to give good + # performance on the common case of creating a double. + increment = 5 + i = 1 + line = nil + + while !line + stack = caller(i, increment) + raise "No non-lib lines in stack" unless stack + + line = stack.find { |l| l !~ LIB_REGEX } + + i += increment + increment *= 2 # The choice of two here is arbitrary. + end + + line + end + else + # Earlier rubies do not support the two argument form of `caller`. This + # fallback is logically the same, but slower. + def self.first_non_rspec_line + caller.find { |line| line !~ LIB_REGEX } + end + end + end +end diff --git a/lib/rspec/mocks/caller_filter.rb b/lib/rspec/mocks/caller_filter.rb deleted file mode 100644 index 00befbc5e..000000000 --- a/lib/rspec/mocks/caller_filter.rb +++ /dev/null @@ -1,58 +0,0 @@ -module RSpec - module Mocks - - # Consistent implementation for "cleaning" the caller method to strip out - # non-rspec lines. This enables errors to be reported at the call site in - # the code using the library, which is far more useful than the particular - # internal method that raised an error. - class CallerFilter - - # This list is an unfortunate dependency on other RSpec core libraries. - # It would be nice if this was not needed. - RSPEC_LIBS = %w[ - core - mocks - expectations - matchers - rails - ] - - LIB_REGEX = %r{/lib/rspec/(#{RSPEC_LIBS.join('|')})(\.rb|/)} - - if RUBY_VERSION >= '2.0.0' - def self.first_non_rspec_line - # `caller` is an expensive method that scales linearly with the size of - # the stack. The performance hit for fetching it in chunks is small, - # and since the target line is probably near the top of the stack, the - # overall improvement of a chunked search like this is significant. - # - # See benchmarks/caller.rb for measurements. - - # Initial value here is mostly arbitrary, but is chosen to give good - # performance on the common case of creating a double. - increment = 5 - i = 1 - line = nil - - while !line - stack = caller(i, increment) - raise "No non-lib lines in stack" unless stack - - line = stack.find { |l| l !~ LIB_REGEX } - - i += increment - increment *= 2 # The choice of two here is arbitrary. - end - - line - end - else - # Earlier rubies do not support the two argument form of `caller`. This - # fallback is logically the same, but slower. - def self.first_non_rspec_line - caller.find { |line| line !~ LIB_REGEX } - end - end - end - end -end diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index e098a015b..18aecff2f 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -2,7 +2,7 @@ # supports wrapping rspec's mocking functionality without invading every # object in the system. -require 'rspec/mocks/caller_filter' +require 'rspec/caller_filter' require 'rspec/mocks/deprecation' require 'rspec/mocks/instance_method_stasher' require 'rspec/mocks/method_double' diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index d8e30a808..a4c2a09c0 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -55,4 +55,17 @@ expect(RSpec::Mocks.configuration).to be(RSpec::Mocks.configuration) end end + + def file_contents_for(lib, filename) + path = $LOAD_PATH.find { |p| p.include?("/#{lib}/lib") } + file = File.join(path, filename) + File.read(file) + end + + it 'has an up-to-date rspec/caller_filter file' do + mocks = file_contents_for("rspec-mocks", "rspec/caller_filter.rb") + core = file_contents_for("rspec-core", "rspec/caller_filter.rb") + + expect(mocks).to eq(core) + end end From 388abc608f85a8bf909cf3b4f5bf96993bc1892c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 23 Aug 2013 00:14:26 -0700 Subject: [PATCH 0189/1179] Remove the benchmark -- it's been added to rspec-core. --- benchmarks/caller.rb | 81 -------------------------------------------- 1 file changed, 81 deletions(-) delete mode 100644 benchmarks/caller.rb diff --git a/benchmarks/caller.rb b/benchmarks/caller.rb deleted file mode 100644 index bc8f7c0d3..000000000 --- a/benchmarks/caller.rb +++ /dev/null @@ -1,81 +0,0 @@ -$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) - -require 'benchmark' -require 'rspec/mocks' - -n = 10000 - -puts "#{n} times - ruby #{RUBY_VERSION}" -puts - -puts "* Using a chunked fetch is quicker than the old method of array-access." -Benchmark.bm(20) do |bm| - bm.report("CallerFilter") do - n.times do - RSpec::Mocks::CallerFilter.first_non_rspec_line - end - end - - bm.report("Direct caller access") do - n.times do - caller(1)[4] - end - end -end - -puts -puts "* Chunking fetches of caller adds a ~17% overhead." -Benchmark.bm(20) do |bm| - bm.report("Chunked") do - n.times do - caller(1, 2) - caller(3, 2) - caller(5, 2) - end - end - - bm.report("All at once") do - n.times do - caller(1, 6) - end - end -end - -puts -puts "* `caller` scales linearly with length parameter." -Benchmark.bm(20) do |bm| - (1..10).each do |x| - bm.report(x) do - n.times do - caller(1, x) - end - end - end -end - - -# > ruby benchmarks/caller.rb -# 10000 times - ruby 2.0.0 -# -# * Using a chunked fetch is quicker than the old method of array-access. -# user system total real -# CallerFilter 0.140000 0.010000 0.150000 ( 0.145381) -# Direct caller access 0.170000 0.000000 0.170000 ( 0.180610) -# -# * Chunking fetches of caller adds a ~17% overhead. -# user system total real -# Chunked 0.150000 0.000000 0.150000 ( 0.181162) -# All at once 0.130000 0.010000 0.140000 ( 0.138732) -# -# * `caller` scales linearly with length parameter. -# user system total real -# 1 0.030000 0.000000 0.030000 ( 0.035000) -# 2 0.050000 0.000000 0.050000 ( 0.059879) -# 3 0.080000 0.000000 0.080000 ( 0.098468) -# 4 0.090000 0.010000 0.100000 ( 0.097619) -# 5 0.110000 0.000000 0.110000 ( 0.126220) -# 6 0.130000 0.000000 0.130000 ( 0.136739) -# 7 0.150000 0.000000 0.150000 ( 0.159055) -# 8 0.160000 0.010000 0.170000 ( 0.172416) -# 9 0.180000 0.000000 0.180000 ( 0.203038) -# 10 0.200000 0.000000 0.200000 ( 0.210551) From 7c976b29278a9eced5c069edc556431781c87725 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 23 Aug 2013 07:14:10 -0700 Subject: [PATCH 0190/1179] Fix load path search. It worked fine before with a local bundler path, but not a :git path -- the :git path includes the SHA in the directory. --- spec/rspec/mocks_spec.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index a4c2a09c0..af9f84ae9 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -57,14 +57,15 @@ end def file_contents_for(lib, filename) - path = $LOAD_PATH.find { |p| p.include?("/#{lib}/lib") } + # http://rubular.com/r/HYpUMftlG2 + path = $LOAD_PATH.find { |p| p.match(/\/rspec-#{lib}(-[a-f0-9]+)?\/lib/) } file = File.join(path, filename) File.read(file) end it 'has an up-to-date rspec/caller_filter file' do - mocks = file_contents_for("rspec-mocks", "rspec/caller_filter.rb") - core = file_contents_for("rspec-core", "rspec/caller_filter.rb") + mocks = file_contents_for("mocks", "rspec/caller_filter.rb") + core = file_contents_for("core", "rspec/caller_filter.rb") expect(mocks).to eq(core) end From c87781b509c47b266acdd99a9fd8acd5cdd6e821 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 23 Aug 2013 07:25:16 -0700 Subject: [PATCH 0191/1179] Improve travis build. - No need to install bundler twice. - No need to bundle install twice. - Skip one-by-one specs on JRuby. - Run cucumber using our standalone bundle (rather than bundle exec). - Bundler issue #2383 has been resolved, no need to work around it anymore. --- .travis.yml | 2 -- script/test_all | 33 ++++++++++++++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index e33f390e3..db5172e3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -before_install: - - gem install bundler bundler_args: "--binstubs --standalone --without documentation" script: "script/test_all" rvm: diff --git a/script/test_all b/script/test_all index cab05ad39..ec8156c91 100755 --- a/script/test_all +++ b/script/test_all @@ -2,8 +2,13 @@ set -e -x -# Needed by Bundler 1.3: https://github.com/carlhuda/bundler/issues/2382 -export RUBYOPT='-rrbconfig' +function is_jruby() { + if ruby -e 'exit RUBY_PLATFORM == "java"'; then + return 0 + else + return 1 + fi +} # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived @@ -12,9 +17,6 @@ export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived # idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' -echo "Bundling Standalone so we can run the specs w/o bundler loaded" -bundle install --standalone --binstubs - echo "Running all..." bin/rspec spec --format progress --profile @@ -22,12 +24,17 @@ echo echo "--------------------------------------------------------------------" echo -for file in `find spec -iname '*_spec.rb'`; do - NO_COVERALLS=1 bin/rspec $file --format progress -done +if is_jruby; then + echo "Skipping one-by-one spec runs due to expensive JVM load time" +else + for file in `find spec -iname '*_spec.rb'`; do + NO_COVERALLS=1 bin/rspec $file -b --format progress + done +fi + +# Prepare RUBYOPT for scenarios that are shelling out to ruby, +# and PATH for those that are using `rspec` or `rake`. +RUBYOPT="-I${PWD}/bundle -rbundler/setup" \ + PATH="${PWD}/bin:$PATH" \ + bin/cucumber --strict -# TODO: it would be nice to figure out how to run the cukes w/o the overhead of -# bundler, but just running `bin/cucumber` can fail due to the fact that it -# shells out (via aruba) and executes `rspec`--which can pick up the wrong -# rspec version if we're not running with bundler. -bundle exec cucumber --strict From cd2c6901a1f56421a4c05053a8c4651447244880 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 25 Aug 2013 19:33:10 -0700 Subject: [PATCH 0192/1179] Update caller filter. - Include additional libs. - Don't put it at `rspec/caller_filter`. - Ignore the "same content as rspec-core" spec for now. The way I had this was brittle and is causing problems in rspec-core. In rspec-core I'm trying to update the regex and add some specs but the fact that the file is at the same path in all 3 libs (which seemed like a simple way to ensure only one of them loads) is causing the _wrong_ version to be loaded in rspec-core's specs, so that it's not picking up the changes I've made. Putting the file at the same path in all 3 libs was a terrible idea in retrospect :(. --- lib/rspec/{ => mocks}/caller_filter.rb | 6 +++--- lib/rspec/mocks/framework.rb | 2 +- spec/rspec/mocks_spec.rb | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) rename lib/rspec/{ => mocks}/caller_filter.rb (89%) diff --git a/lib/rspec/caller_filter.rb b/lib/rspec/mocks/caller_filter.rb similarity index 89% rename from lib/rspec/caller_filter.rb rename to lib/rspec/mocks/caller_filter.rb index 4e2dbe22f..14310878a 100644 --- a/lib/rspec/caller_filter.rb +++ b/lib/rspec/mocks/caller_filter.rb @@ -5,8 +5,6 @@ module RSpec # internal method that raised an error. class CallerFilter - # This list is an unfortunate dependency on other RSpec core libraries. - # It would be nice if this was not needed. RSPEC_LIBS = %w[ core mocks @@ -15,7 +13,9 @@ class CallerFilter rails ] - LIB_REGEX = %r{/lib/rspec/(#{RSPEC_LIBS.join('|')})(\.rb|/)} + ADDITIONAL_TOP_LEVEL_FILES = %w[ autorun caller_filter ] + + LIB_REGEX = %r{/lib/rspec/(#{(RSPEC_LIBS + ADDITIONAL_TOP_LEVEL_FILES).join('|')})(\.rb|/)} if RUBY_VERSION >= '2.0.0' def self.first_non_rspec_line diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 18aecff2f..d2fdb4cbe 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -2,7 +2,7 @@ # supports wrapping rspec's mocking functionality without invading every # object in the system. -require 'rspec/caller_filter' +require 'rspec/mocks/caller_filter' unless defined?(::RSpec::CallerFilter) require 'rspec/mocks/deprecation' require 'rspec/mocks/instance_method_stasher' require 'rspec/mocks/method_double' diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index af9f84ae9..fb1e409a2 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -64,6 +64,8 @@ def file_contents_for(lib, filename) end it 'has an up-to-date rspec/caller_filter file' do + pending "Holding off on this spec for now while this settles" + mocks = file_contents_for("mocks", "rspec/caller_filter.rb") core = file_contents_for("core", "rspec/caller_filter.rb") From b76fe2b2a9ded2da8a7f4e3c0f0cc968aa84aae2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 25 Aug 2013 19:59:46 -0700 Subject: [PATCH 0193/1179] Revert how we run cucumber. It's causing a SystemStackError on 1.9.2. I'm not sure why and don't have time to debug it now. --- script/test_all | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/script/test_all b/script/test_all index ec8156c91..2143261c5 100755 --- a/script/test_all +++ b/script/test_all @@ -32,9 +32,9 @@ else done fi -# Prepare RUBYOPT for scenarios that are shelling out to ruby, -# and PATH for those that are using `rspec` or `rake`. -RUBYOPT="-I${PWD}/bundle -rbundler/setup" \ - PATH="${PWD}/bin:$PATH" \ - bin/cucumber --strict +# TODO: it would be nice to figure out how to run the cukes w/o the overhead of +# bundler, but just running `bin/cucumber` can fail due to the fact that it +# shells out (via aruba) and executes `rspec`--which can pick up the wrong +# rspec version if we're not running with bundler. +bundle exec cucumber --strict From f3d70e1ef20eceeb42d1f97099a5af020e4a8726 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 25 Aug 2013 22:41:35 -0700 Subject: [PATCH 0194/1179] Update caller filter once more... --- lib/rspec/mocks/caller_filter.rb | 2 +- spec/rspec/mocks_spec.rb | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/caller_filter.rb b/lib/rspec/mocks/caller_filter.rb index 14310878a..a3c29dd4e 100644 --- a/lib/rspec/mocks/caller_filter.rb +++ b/lib/rspec/mocks/caller_filter.rb @@ -13,7 +13,7 @@ class CallerFilter rails ] - ADDITIONAL_TOP_LEVEL_FILES = %w[ autorun caller_filter ] + ADDITIONAL_TOP_LEVEL_FILES = %w[ autorun ] LIB_REGEX = %r{/lib/rspec/(#{(RSPEC_LIBS + ADDITIONAL_TOP_LEVEL_FILES).join('|')})(\.rb|/)} diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index fb1e409a2..e8723db80 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -64,10 +64,8 @@ def file_contents_for(lib, filename) end it 'has an up-to-date rspec/caller_filter file' do - pending "Holding off on this spec for now while this settles" - - mocks = file_contents_for("mocks", "rspec/caller_filter.rb") - core = file_contents_for("core", "rspec/caller_filter.rb") + mocks = file_contents_for("mocks", "rspec/mocks/caller_filter.rb") + core = file_contents_for("core", "rspec/core/caller_filter.rb") expect(mocks).to eq(core) end From 3ac6f4e0155db12aa1ecbfe20d710e2d90c581fa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 25 Aug 2013 23:23:05 -0700 Subject: [PATCH 0195/1179] Correct inaccurate doc string. --- spec/rspec/mocks_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index e8723db80..d46c58eeb 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -63,7 +63,7 @@ def file_contents_for(lib, filename) File.read(file) end - it 'has an up-to-date rspec/caller_filter file' do + it 'has an up-to-date caller_filter file' do mocks = file_contents_for("mocks", "rspec/mocks/caller_filter.rb") core = file_contents_for("core", "rspec/core/caller_filter.rb") From 9571e78ed2a6a4461fcc872cbb7eec6ccb666ee2 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 9 Aug 2013 21:18:38 +1000 Subject: [PATCH 0196/1179] delegate receive_messages to a ReceivesMessages matcher, complaining if a block is given --- lib/rspec/mocks/framework.rb | 1 + lib/rspec/mocks/syntax.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index d2fdb4cbe..7b4eafefd 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -28,6 +28,7 @@ require 'rspec/mocks/mutate_const' require 'rspec/mocks/matchers/have_received' require 'rspec/mocks/matchers/receive' +require 'rspec/mocks/matchers/receives_messages' require 'rspec/mocks/stub_chain' require 'rspec/mocks/targets' require 'rspec/mocks/syntax' diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index a2c2b5d2c..451375194 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -92,6 +92,11 @@ def receive(method_name, &block) Matchers::Receive.new(method_name, block) end + def receive_messages(method_value_hash) + raise "Implementation blocks arn't supported with `receive_messages`" if block_given? + Matchers::ReceivesMessages.new(method_value_hash) + end + def allow(target) AllowanceTarget.new(target) end From d6498b417775e13bad1e7f012dd344e954c128e8 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 9 Aug 2013 21:18:56 +1000 Subject: [PATCH 0197/1179] allow the receivesmessages matchers with allow syntax --- lib/rspec/mocks/targets.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 88aa5928e..cccc36915 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -11,7 +11,7 @@ def initialize(target) def self.delegate_to(matcher_method, options = {}) method_name = options.fetch(:from) { :to } define_method(method_name) do |matcher, &block| - unless Matchers::Receive === matcher + unless Matchers::Receive === matcher || Matchers::ReceivesMessages === matcher raise UnsupportedMatcherError, "only the `receive` matcher is supported " + "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" end From 305f460fada8244d7e762b2666807ebad6618400 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 9 Aug 2013 21:34:45 +1000 Subject: [PATCH 0198/1179] matcher to setup multiple expectations/allowances in short form --- lib/rspec/mocks/matchers/receives_messages.rb | 37 +++++++++ .../mocks/matchers/receive_messages_spec.rb | 77 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 lib/rspec/mocks/matchers/receives_messages.rb create mode 100644 spec/rspec/mocks/matchers/receive_messages_spec.rb diff --git a/lib/rspec/mocks/matchers/receives_messages.rb b/lib/rspec/mocks/matchers/receives_messages.rb new file mode 100644 index 000000000..a5bd2e65a --- /dev/null +++ b/lib/rspec/mocks/matchers/receives_messages.rb @@ -0,0 +1,37 @@ +module RSpec + module Mocks + module Matchers + class ReceivesMessages + + def initialize(message_value_hash) + @receivers = message_value_hash.map do |method_name, value| + Matchers::Receive.new(method_name, proc { value }) + end + end + + %w[ + setup_expecation matches? setup_negative_expectation does_not_match? + setup_allowance setup_any_instance_expectation setup_any_instance_expectation + setup_any_instance_negative_expectation setup_any_instance_allowance + ].each do |method_name| + define_method(method_name) do |subject, &block| + @receivers.each do |receiver| + receiver.send(method_name, subject, &block) + end + end + end + + MessageExpectation.public_instance_methods(false).each do |method_name| + next if method_defined?(method_name) + + define_method(method_name) do |*args, &block| + @receivers.each do |receiver| + receiver.send(method_name, *args, &block) + end + self + end + end + end + end + end +end diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb new file mode 100644 index 000000000..f97959020 --- /dev/null +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' + +module RSpec + module Mocks + describe "allow(...).to receive_messages(:a => 1, :b => 2)" do + let(:obj) { double "Object" } + + it "allows the object to respond to multiple messages" do + allow(obj).to receive_messages(:a => 1, :b => 2) + expect(obj.a).to eq 1 + expect(obj.b).to eq 2 + end + + it "allows single expectations" do + allow(obj).to receive_messages(:a => 1) + expect(obj.a).to eq 1 + end + + it "complains if a block is given" do + expect do + allow(obj).to receive_messages(:a => 1) { "implementation" } + end.to raise_error "Implementation blocks arn't supported with `receive_messages`" + end + end + + describe "expect(...).to receive_messages(:a => 1, :b => 2)" do + let(:reporter) { RSpec::Core::Reporter.new } + + it "sets up multiple expectations" do + expect(reporter).to receive(:example_passed).with("will pass") + expect(reporter).to receive(:example_failed).with("will fail") + + example_group = ::RSpec::Core::ExampleGroup.describe do + before do + obj = double "Object" + expect(obj).to receive_messages(:a => 1, :b => 2) + end + + it "will pass" do + obj.a && obj.b + end + + it "will fail" do + obj.a + end + end + example_group.run reporter + end + + it "allows single expectations" do + expect(reporter).to receive(:example_passed).with("will pass") + expect(reporter).to receive(:example_failed).with("will fail") + + example_group = ::RSpec::Core::ExampleGroup.describe do + before do + obj = double "Object" + expect(obj).to receive_messages(:a => 1) + end + + it "will pass" do + obj.a + end + + it "will fail" do + end + end + example_group.run reporter + end + + it "complains if a block is given" do + expect do + expect(double).to receive_messages(:a => 1) { "implementation" } + end.to raise_error "Implementation blocks arn't supported with `receive_messages`" + end + end + end +end From a4a3bac601a15e09f93afa7b7e52a5fcaef92c1b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 9 Aug 2013 20:57:06 +0800 Subject: [PATCH 0199/1179] changelog --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 164a5fbd4..1b084f864 100644 --- a/Changelog.md +++ b/Changelog.md @@ -30,6 +30,8 @@ Enhancements: attributes. (Xavier Shay) * Default value of `transfer_nested_constants` option for constant stubbing can be configured. (Xavier Shay) +* Messages can be allowed or expected on in bulk via + `receive_messages(:message => :value)` (Jon Rowe) Bug Fixes: From 53cde822d767835149b0470758ad5b0c59bb56ce Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 10 Aug 2013 00:18:57 +0800 Subject: [PATCH 0200/1179] update unsupported target message for matchers --- lib/rspec/mocks/targets.rb | 3 ++- spec/rspec/mocks/matchers/receive_spec.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index cccc36915..2c84fc438 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -12,7 +12,8 @@ def self.delegate_to(matcher_method, options = {}) method_name = options.fetch(:from) { :to } define_method(method_name) do |matcher, &block| unless Matchers::Receive === matcher || Matchers::ReceivesMessages === matcher - raise UnsupportedMatcherError, "only the `receive` matcher is supported " + + raise UnsupportedMatcherError, + "only the `receive` or `receive_messages` matchers are supported " + "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" end diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index bab175031..f5590f062 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -265,7 +265,7 @@ def eq(value) framework.new.instance_exec do expect(3).to eq(3) end - }.to raise_error(/only the `receive` matcher is supported with `expect\(...\).to`/) + }.to raise_error(/only the `receive` or `receive_messages` matchers are supported with `expect\(...\).to`/) end it 'can toggle the available syntax' do From 9ef017d34a03dbd437d46f4821075a499908915c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 10 Aug 2013 00:31:38 +0800 Subject: [PATCH 0201/1179] refactor specs to use verify and include any_instance_of examples --- .../mocks/matchers/receive_messages_spec.rb | 70 ++++++++----------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index f97959020..b116831c3 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -11,65 +11,57 @@ module Mocks expect(obj.b).to eq 2 end - it "allows single expectations" do - allow(obj).to receive_messages(:a => 1) + it "complains if a block is given" do + expect do + allow(obj).to receive_messages(:a => 1) { "implementation" } + end.to raise_error "Implementation blocks arn't supported with `receive_messages`" + end + end + + describe "allow_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do + let(:obj) { Object.new } + + it "allows the object to respond to multiple messages" do + allow_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) expect(obj.a).to eq 1 + expect(obj.b).to eq 2 end it "complains if a block is given" do expect do - allow(obj).to receive_messages(:a => 1) { "implementation" } + allow_any_instance_of(Object).to receive_messages(:a => 1) { "implementation" } end.to raise_error "Implementation blocks arn't supported with `receive_messages`" end end describe "expect(...).to receive_messages(:a => 1, :b => 2)" do - let(:reporter) { RSpec::Core::Reporter.new } + let(:obj) { double "Object" } it "sets up multiple expectations" do - expect(reporter).to receive(:example_passed).with("will pass") - expect(reporter).to receive(:example_failed).with("will fail") - - example_group = ::RSpec::Core::ExampleGroup.describe do - before do - obj = double "Object" - expect(obj).to receive_messages(:a => 1, :b => 2) - end - - it "will pass" do - obj.a && obj.b - end - - it "will fail" do - obj.a - end - end - example_group.run reporter + expect(obj).to receive_messages(:a => 1, :b => 2) + obj.a + expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError end - it "allows single expectations" do - expect(reporter).to receive(:example_passed).with("will pass") - expect(reporter).to receive(:example_failed).with("will fail") - - example_group = ::RSpec::Core::ExampleGroup.describe do - before do - obj = double "Object" - expect(obj).to receive_messages(:a => 1) - end + it "complains if a block is given" do + expect do + expect(double).to receive_messages(:a => 1) { "implementation" } + end.to raise_error "Implementation blocks arn't supported with `receive_messages`" + end + end - it "will pass" do - obj.a - end + describe "expect_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do + let(:obj) { Object.new } - it "will fail" do - end - end - example_group.run reporter + it "sets up multiple expectations" do + expect_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) + obj.a + expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError end it "complains if a block is given" do expect do - expect(double).to receive_messages(:a => 1) { "implementation" } + expect_any_instance_of(Object).to receive_messages(:a => 1) { "implementation" } end.to raise_error "Implementation blocks arn't supported with `receive_messages`" end end From ca02ce25312872ce3968c752b0edc0d84075614b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 10 Aug 2013 02:04:13 +0100 Subject: [PATCH 0202/1179] cleanup receive_messages when disabling expect --- lib/rspec/mocks/syntax.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 451375194..6acc958f9 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -124,6 +124,7 @@ def self.disable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods) syntax_host.class_exec do undef receive + undef receive_messages undef allow undef expect_any_instance_of undef allow_any_instance_of From 2423599631ff938818ad4e9ad3805897ba5d6349 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 10 Aug 2013 16:16:12 +0100 Subject: [PATCH 0203/1179] switch to curlie braces --- .../mocks/matchers/receive_messages_spec.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index b116831c3..49926a669 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -12,9 +12,9 @@ module Mocks end it "complains if a block is given" do - expect do + expect { allow(obj).to receive_messages(:a => 1) { "implementation" } - end.to raise_error "Implementation blocks arn't supported with `receive_messages`" + }.to raise_error "Implementation blocks arn't supported with `receive_messages`" end end @@ -28,9 +28,9 @@ module Mocks end it "complains if a block is given" do - expect do + expect { allow_any_instance_of(Object).to receive_messages(:a => 1) { "implementation" } - end.to raise_error "Implementation blocks arn't supported with `receive_messages`" + }.to raise_error "Implementation blocks arn't supported with `receive_messages`" end end @@ -44,9 +44,9 @@ module Mocks end it "complains if a block is given" do - expect do + expect { expect(double).to receive_messages(:a => 1) { "implementation" } - end.to raise_error "Implementation blocks arn't supported with `receive_messages`" + }.to raise_error "Implementation blocks arn't supported with `receive_messages`" end end @@ -60,9 +60,9 @@ module Mocks end it "complains if a block is given" do - expect do + expect { expect_any_instance_of(Object).to receive_messages(:a => 1) { "implementation" } - end.to raise_error "Implementation blocks arn't supported with `receive_messages`" + }.to raise_error "Implementation blocks arn't supported with `receive_messages`" end end end From 9e1044388002591a8064a69d0476b1b49f44cde9 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 10 Aug 2013 16:26:51 +0100 Subject: [PATCH 0204/1179] yard docs for receive_messages --- lib/rspec/mocks/syntax.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 6acc958f9..49065311e 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -327,6 +327,19 @@ def self.default_should_syntax_host # expect(obj).to receive(:hello).with("world").exactly(3).times # # @note This is only available when you have enabled the `expect` syntax. + # + # @method receive_messages + # Shorthand syntax used to setup message(s), and their return value(s), + # that you expect or allow an object to receive. The method takes a hash + # of messages and their respective return values. Unlike `receive` block + # implementations are not supported, neither is the fluent interface. + # + # @example + # + # allow(obj).to receive_messages(:speak => "Hello World") + # allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow") + # + # @note This is only available when you have enabled the `expect` syntax. end end end From 549c93085e818109c7c1d2fcc50ad80942ca17f8 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 12 Aug 2013 22:05:15 +0100 Subject: [PATCH 0205/1179] refactor simple expectation to allow use as an expectation as well as an allowance --- lib/rspec/mocks/error_generator.rb | 5 +++++ lib/rspec/mocks/message_expectation.rb | 21 ++++++++++++++++++--- lib/rspec/mocks/method_double.rb | 22 ++++++++++++++++------ lib/rspec/mocks/proxy.rb | 5 +++++ 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index d325017fd..3bc6f2925 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -47,6 +47,11 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc __raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}" end + # @private + def raise_simple_expectation_error(message) + __raise "(#{intro}).#{message} expected with any arguments" + end + # @private def raise_unimplemented_error(doubled_module, method_name) __raise "%s does not implement:\n %s" % [ diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 893ad737e..42cff7c88 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -5,13 +5,28 @@ module Mocks # for a message. While this same effect can be achieved using a standard # MessageExpecation, this version is much faster and so can be used as an # optimization. - SimpleMessageExpectation = Struct.new(:message, :response) do + class SimpleMessageExpectation + + def initialize(message, response, error_generator) + @message, @response, @error_generator = message, response, error_generator + @received = false + end + def invoke(*_) - response + @received = true + @response end def matches?(message, *_) - self.message == message + @message == message + end + + def called_max_times? + false + end + + def verify_messages_received + @error_generator.raise_simple_expectation_error(@message) unless @received end end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 91cab207e..42f5732b1 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -139,18 +139,28 @@ def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &imp # A simple stub can only return a concrete value for a message, and # cannot match on arguments. It is used as an optimization over - # `add_stub` where it is known in advance that this is all that will be - # required of a stub, such as when passing attributes to the `double` - # example method. They do not stash or restore existing method + # `add_stub` / `add_expectation` where it is known in advance that this + # is all that will be required of a stub, such as when passing attributes + # to the `double` example method. They do not stash or restore existing method # definitions. # # @private def add_simple_stub(method_name, response) + setup_simple_method_double method_name, response, stubs + end + + # @private + def add_simple_expectation(method_name, response, error_generator) + setup_simple_method_double method_name, response, expectations, error_generator + end + + # @private + def setup_simple_method_double(method_name, response, collection, error_generator = nil) define_proxy_method - stub = SimpleMessageExpectation.new(method_name, response) - stubs.unshift stub - stub + me = SimpleMessageExpectation.new(method_name, response, error_generator) + collection.unshift me + me end # @private diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index f2832a5f0..f922d6429 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -59,6 +59,11 @@ def add_message_expectation(location, method_name, opts={}, &block) meth_double.add_expectation @error_generator, @expectation_ordering, location, opts, &block end + # @private + def add_simple_expectation(method_name, response) + method_double[method_name].add_simple_expectation method_name, response, @error_generator + end + # @private def build_expectation(method_name) meth_double = method_double[method_name] From 8611473599ed49f2115edac02f29641e39d67633 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 12 Aug 2013 22:06:26 +0100 Subject: [PATCH 0206/1179] refactor receive messages to use the simple stub/expectation when available --- lib/rspec/mocks/matchers/receives_messages.rb | 62 +++++++++++++------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/lib/rspec/mocks/matchers/receives_messages.rb b/lib/rspec/mocks/matchers/receives_messages.rb index a5bd2e65a..0c4441760 100644 --- a/lib/rspec/mocks/matchers/receives_messages.rb +++ b/lib/rspec/mocks/matchers/receives_messages.rb @@ -4,33 +4,55 @@ module Matchers class ReceivesMessages def initialize(message_value_hash) - @receivers = message_value_hash.map do |method_name, value| - Matchers::Receive.new(method_name, proc { value }) - end + @message_value_hash = message_value_hash end - %w[ - setup_expecation matches? setup_negative_expectation does_not_match? - setup_allowance setup_any_instance_expectation setup_any_instance_expectation - setup_any_instance_negative_expectation setup_any_instance_allowance - ].each do |method_name| - define_method(method_name) do |subject, &block| - @receivers.each do |receiver| - receiver.send(method_name, subject, &block) - end - end + def setup_expectation(subject, &block) + map_to proxy_on(subject), :add_simple_expectation + end + alias matches? setup_expectation + alias does_not_match? setup_expectation + alias setup_negative_expectation setup_expectation + alias setup_allowance setup_expectation + + def setup_allowance(subject) + map_to proxy_on(subject), :add_simple_stub + end + + def setup_any_instance_expectation(subject) + map_to_as_chain any_instance_of(subject), :should_receive + end + + def setup_any_instance_negative_expectation(subject) + map_to_as_chain any_instance_of(subject), :should_not_receive + end + + def setup_any_instance_allowance(subject) + any_instance_of(subject).stub(@message_value_hash) end - MessageExpectation.public_instance_methods(false).each do |method_name| - next if method_defined?(method_name) + private + + def proxy_on(subject) + ::RSpec::Mocks.proxy_for(subject) + end + + def any_instance_of(subject) + ::RSpec::Mocks.any_instance_recorder_for(subject) + end - define_method(method_name) do |*args, &block| - @receivers.each do |receiver| - receiver.send(method_name, *args, &block) - end - self + def map_to(host, method_name) + @message_value_hash.each do |message, value| + host.__send__(method_name, message.to_sym, value) end end + + def map_to_as_chain(host, method_name, *args) + @message_value_hash.each do |message, value| + host.__send__(method_name, message, *args).and_return(value) + end + end + end end end From 2e77b1bfd43f21e639b7f6660607b3f23803fcf7 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 12 Aug 2013 22:06:33 +0100 Subject: [PATCH 0207/1179] cleanup a backtrack line --- lib/rspec/mocks/matchers/receive.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index f4ea73ede..dee4413d9 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -11,9 +11,7 @@ def initialize(message, block) # reports an extra "in `new'" line in the backtrace that the # others do not include. The safest way to find the right # line is to search for the first line BEFORE rspec/mocks/syntax.rb. - @backtrace_line = caller.find do |line| - !line.split(':').first.end_with?('rspec/mocks/syntax.rb') - end + @backtrace_line = CallerFilter.first_non_rspec_line end def setup_expectation(subject, &block) From f83fde0a15b935cf7b1f271d1b486ecc075a2a4b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 13 Aug 2013 08:39:20 +0100 Subject: [PATCH 0208/1179] refactor to remove negative match and clean up iterator --- lib/rspec/mocks/matchers/receives_messages.rb | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/lib/rspec/mocks/matchers/receives_messages.rb b/lib/rspec/mocks/matchers/receives_messages.rb index 0c4441760..7121c445b 100644 --- a/lib/rspec/mocks/matchers/receives_messages.rb +++ b/lib/rspec/mocks/matchers/receives_messages.rb @@ -3,32 +3,31 @@ module Mocks module Matchers class ReceivesMessages - def initialize(message_value_hash) - @message_value_hash = message_value_hash + def initialize(message_return_value_hash) + @message_return_value_hash = message_return_value_hash end def setup_expectation(subject, &block) - map_to proxy_on(subject), :add_simple_expectation + each_message_on( proxy_on(subject) ) do |host, message, return_value| + host.add_simple_expectation(message, return_value) + end end - alias matches? setup_expectation - alias does_not_match? setup_expectation - alias setup_negative_expectation setup_expectation - alias setup_allowance setup_expectation + alias matches? setup_expectation def setup_allowance(subject) - map_to proxy_on(subject), :add_simple_stub + each_message_on( proxy_on(subject) ) do |host, message, return_value| + host.add_simple_stub(message, return_value) + end end def setup_any_instance_expectation(subject) - map_to_as_chain any_instance_of(subject), :should_receive - end - - def setup_any_instance_negative_expectation(subject) - map_to_as_chain any_instance_of(subject), :should_not_receive + each_message_on( any_instance_of(subject) ) do |host, message, return_value| + host.should_receive(message).and_return(return_value) + end end def setup_any_instance_allowance(subject) - any_instance_of(subject).stub(@message_value_hash) + any_instance_of(subject).stub(@message_return_value_hash) end private @@ -41,15 +40,9 @@ def any_instance_of(subject) ::RSpec::Mocks.any_instance_recorder_for(subject) end - def map_to(host, method_name) - @message_value_hash.each do |message, value| - host.__send__(method_name, message.to_sym, value) - end - end - - def map_to_as_chain(host, method_name, *args) - @message_value_hash.each do |message, value| - host.__send__(method_name, message, *args).and_return(value) + def each_message_on(host) + @message_return_value_hash.each do |message, value| + yield host, message, value end end From 2238f666c40a7b71c1d3c9807d07c141b32712d2 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 13 Aug 2013 08:42:42 +0100 Subject: [PATCH 0209/1179] rename to singular receive --- lib/rspec/mocks/framework.rb | 2 +- .../matchers/{receives_messages.rb => receive_messages.rb} | 2 +- lib/rspec/mocks/syntax.rb | 2 +- lib/rspec/mocks/targets.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename lib/rspec/mocks/matchers/{receives_messages.rb => receive_messages.rb} (97%) diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 7b4eafefd..f6e92a330 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -28,7 +28,7 @@ require 'rspec/mocks/mutate_const' require 'rspec/mocks/matchers/have_received' require 'rspec/mocks/matchers/receive' -require 'rspec/mocks/matchers/receives_messages' +require 'rspec/mocks/matchers/receive_messages' require 'rspec/mocks/stub_chain' require 'rspec/mocks/targets' require 'rspec/mocks/syntax' diff --git a/lib/rspec/mocks/matchers/receives_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb similarity index 97% rename from lib/rspec/mocks/matchers/receives_messages.rb rename to lib/rspec/mocks/matchers/receive_messages.rb index 7121c445b..a444b8f28 100644 --- a/lib/rspec/mocks/matchers/receives_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module Matchers - class ReceivesMessages + class ReceiveMessages def initialize(message_return_value_hash) @message_return_value_hash = message_return_value_hash diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 49065311e..ae9ca6802 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -94,7 +94,7 @@ def receive(method_name, &block) def receive_messages(method_value_hash) raise "Implementation blocks arn't supported with `receive_messages`" if block_given? - Matchers::ReceivesMessages.new(method_value_hash) + Matchers::ReceiveMessages.new(method_value_hash) end def allow(target) diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 2c84fc438..8d08c5cc6 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -11,7 +11,7 @@ def initialize(target) def self.delegate_to(matcher_method, options = {}) method_name = options.fetch(:from) { :to } define_method(method_name) do |matcher, &block| - unless Matchers::Receive === matcher || Matchers::ReceivesMessages === matcher + unless Matchers::Receive === matcher || Matchers::ReceiveMessages === matcher raise UnsupportedMatcherError, "only the `receive` or `receive_messages` matchers are supported " + "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" From 4566fc651752ef8fdbc72878cd2984527f35e7a6 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 13 Aug 2013 08:49:11 +0100 Subject: [PATCH 0210/1179] grammar --- lib/rspec/mocks/syntax.rb | 8 ++++---- spec/rspec/mocks/matchers/receive_messages_spec.rb | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index ae9ca6802..f4948038e 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -92,9 +92,9 @@ def receive(method_name, &block) Matchers::Receive.new(method_name, block) end - def receive_messages(method_value_hash) - raise "Implementation blocks arn't supported with `receive_messages`" if block_given? - Matchers::ReceiveMessages.new(method_value_hash) + def receive_messages(message_return_value_hash) + raise "Implementation blocks aren't supported with `receive_messages`" if block_given? + Matchers::ReceiveMessages.new(message_return_value_hash) end def allow(target) @@ -331,7 +331,7 @@ def self.default_should_syntax_host # @method receive_messages # Shorthand syntax used to setup message(s), and their return value(s), # that you expect or allow an object to receive. The method takes a hash - # of messages and their respective return values. Unlike `receive` block + # of messages and their respective return values. Unlike `receive`, block # implementations are not supported, neither is the fluent interface. # # @example diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 49926a669..047efbbef 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -14,7 +14,7 @@ module Mocks it "complains if a block is given" do expect { allow(obj).to receive_messages(:a => 1) { "implementation" } - }.to raise_error "Implementation blocks arn't supported with `receive_messages`" + }.to raise_error "Implementation blocks aren't supported with `receive_messages`" end end @@ -30,7 +30,7 @@ module Mocks it "complains if a block is given" do expect { allow_any_instance_of(Object).to receive_messages(:a => 1) { "implementation" } - }.to raise_error "Implementation blocks arn't supported with `receive_messages`" + }.to raise_error "Implementation blocks aren't supported with `receive_messages`" end end @@ -46,7 +46,7 @@ module Mocks it "complains if a block is given" do expect { expect(double).to receive_messages(:a => 1) { "implementation" } - }.to raise_error "Implementation blocks arn't supported with `receive_messages`" + }.to raise_error "Implementation blocks aren't supported with `receive_messages`" end end @@ -62,7 +62,7 @@ module Mocks it "complains if a block is given" do expect { expect_any_instance_of(Object).to receive_messages(:a => 1) { "implementation" } - }.to raise_error "Implementation blocks arn't supported with `receive_messages`" + }.to raise_error "Implementation blocks aren't supported with `receive_messages`" end end end From ea7885de80105c9c04c2ed6c0ca2dd24dd73708a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 13 Aug 2013 14:33:51 +0100 Subject: [PATCH 0211/1179] remove simple expectation error message --- lib/rspec/mocks/error_generator.rb | 5 ----- lib/rspec/mocks/message_expectation.rb | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 3bc6f2925..d325017fd 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -47,11 +47,6 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc __raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}" end - # @private - def raise_simple_expectation_error(message) - __raise "(#{intro}).#{message} expected with any arguments" - end - # @private def raise_unimplemented_error(doubled_module, method_name) __raise "%s does not implement:\n %s" % [ diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 42cff7c88..25e1ee560 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -26,7 +26,9 @@ def called_max_times? end def verify_messages_received - @error_generator.raise_simple_expectation_error(@message) unless @received + unless @received + @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil) + end end end From d1f72dddeddaa4bb75124a5fa85c520fc7d8d14e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 13 Aug 2013 15:01:45 +0100 Subject: [PATCH 0212/1179] assert on message and fix backtrace location --- lib/rspec/mocks/matchers/receive_messages.rb | 3 ++- lib/rspec/mocks/message_expectation.rb | 7 +++++-- lib/rspec/mocks/method_double.rb | 8 +++---- lib/rspec/mocks/proxy.rb | 4 ++-- .../mocks/matchers/receive_messages_spec.rb | 21 +++++++++++++++++++ 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index a444b8f28..9d7a0768b 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -5,11 +5,12 @@ class ReceiveMessages def initialize(message_return_value_hash) @message_return_value_hash = message_return_value_hash + @backtrace_line = CallerFilter.first_non_rspec_line end def setup_expectation(subject, &block) each_message_on( proxy_on(subject) ) do |host, message, return_value| - host.add_simple_expectation(message, return_value) + host.add_simple_expectation(message, return_value, @backtrace_line) end end alias matches? setup_expectation diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 25e1ee560..dcce79ff2 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -7,8 +7,8 @@ module Mocks # optimization. class SimpleMessageExpectation - def initialize(message, response, error_generator) - @message, @response, @error_generator = message, response, error_generator + def initialize(message, response, error_generator, backtrace_line = nil) + @message, @response, @error_generator, @backtrace_line = message, response, error_generator, backtrace_line @received = false end @@ -29,6 +29,9 @@ def verify_messages_received unless @received @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil) end + rescue RSpec::Mocks::MockExpectationError => error + error.backtrace.insert(0, @backtrace_line) + Kernel::raise error end end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 42f5732b1..6343b5ab6 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -150,15 +150,15 @@ def add_simple_stub(method_name, response) end # @private - def add_simple_expectation(method_name, response, error_generator) - setup_simple_method_double method_name, response, expectations, error_generator + def add_simple_expectation(method_name, response, error_generator, backtrace_line) + setup_simple_method_double method_name, response, expectations, error_generator, backtrace_line end # @private - def setup_simple_method_double(method_name, response, collection, error_generator = nil) + def setup_simple_method_double(method_name, response, collection, error_generator = nil, backtrace_line = nil) define_proxy_method - me = SimpleMessageExpectation.new(method_name, response, error_generator) + me = SimpleMessageExpectation.new(method_name, response, error_generator, backtrace_line) collection.unshift me me end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index f922d6429..f8141a05a 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -60,8 +60,8 @@ def add_message_expectation(location, method_name, opts={}, &block) end # @private - def add_simple_expectation(method_name, response) - method_double[method_name].add_simple_expectation method_name, response, @error_generator + def add_simple_expectation(method_name, response, location) + method_double[method_name].add_simple_expectation method_name, response, @error_generator, location end # @private diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 047efbbef..72120387d 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -37,12 +37,33 @@ module Mocks describe "expect(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { double "Object" } + let(:expectation_error) do + failure = nil + begin + RSpec::Mocks.space.verify_all + rescue RSpec::Mocks::MockExpectationError => error + failure = error + end + failure + end + + it "sets up multiple expectations" do expect(obj).to receive_messages(:a => 1, :b => 2) obj.a expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError end + it 'fails with a sensible message' do + expect(obj).to receive_messages(:a => 1, :b => 2) + expect(expectation_error.to_s).to eq %Q{(Double "Object").a(no args)\n expected: 1 time with any arguments\n received: 0 times} + end + + it 'fails with the correct location' do + expect(obj).to receive_messages(:a => 1, :b => 2); line = __LINE__ + expect(expectation_error.backtrace[0]).to match /#{__FILE__}:#{line}/ + end + it "complains if a block is given" do expect { expect(double).to receive_messages(:a => 1) { "implementation" } From 476b9a046bd04b3b356a512efb73239426c02c77 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 13 Aug 2013 18:39:43 +0100 Subject: [PATCH 0213/1179] cheat 1.8.7 not ordering hashes --- spec/rspec/mocks/matchers/receive_messages_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 72120387d..cadea4e5f 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -56,6 +56,7 @@ module Mocks it 'fails with a sensible message' do expect(obj).to receive_messages(:a => 1, :b => 2) + obj.b expect(expectation_error.to_s).to eq %Q{(Double "Object").a(no args)\n expected: 1 time with any arguments\n received: 0 times} end From 4f9a9e5f703c901a7f452829e30b28170464f5a8 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 19 Aug 2013 11:26:39 +0100 Subject: [PATCH 0214/1179] correct expression name --- lib/rspec/mocks/targets.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 8d08c5cc6..bbdaa499e 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -51,7 +51,7 @@ class ExpectationTarget < TargetBase end class AnyInstanceAllowanceTarget < TargetBase - EXPRESSION = :expect_any_instance_of + EXPRESSION = :allow_any_instance_of delegate_to :setup_any_instance_allowance disallow_negation :not_to disallow_negation :to_not From 35cce72320939fbd2710abb4ed4193ef8560c58e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 19 Aug 2013 11:31:29 +0100 Subject: [PATCH 0215/1179] refactor matcher messages for unsupported operations --- lib/rspec/mocks/matchers/have_received.rb | 4 ++++ lib/rspec/mocks/matchers/receive.rb | 4 ++++ lib/rspec/mocks/matchers/receive_messages.rb | 4 ++++ lib/rspec/mocks/targets.rb | 24 +++++++++++++------- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 63ae25838..ff0ff7d6e 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -13,6 +13,10 @@ def initialize(method_name, &block) @subject = nil end + def name + "have_received" + end + def matches?(subject, &block) @block ||= block @subject = subject diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index dee4413d9..8b12663b2 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -14,6 +14,10 @@ def initialize(message, block) @backtrace_line = CallerFilter.first_non_rspec_line end + def name + "receive" + end + def setup_expectation(subject, &block) setup_mock_proxy_method_substitute(subject, :add_message_expectation, block) end diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index 9d7a0768b..cabad836c 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -8,6 +8,10 @@ def initialize(message_return_value_hash) @backtrace_line = CallerFilter.first_non_rspec_line end + def name + "receive_messages" + end + def setup_expectation(subject, &block) each_message_on( proxy_on(subject) ) do |host, message, return_value| host.add_simple_expectation(message, return_value, @backtrace_line) diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index bbdaa499e..75ad4da19 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -12,25 +12,33 @@ def self.delegate_to(matcher_method, options = {}) method_name = options.fetch(:from) { :to } define_method(method_name) do |matcher, &block| unless Matchers::Receive === matcher || Matchers::ReceiveMessages === matcher - raise UnsupportedMatcherError, - "only the `receive` or `receive_messages` matchers are supported " + - "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" + raise_unsupported_matcher(:to, matcher) end matcher.__send__(matcher_method, @target, &block) end end - def self.disallow_negation(method) - define_method method do |*args| - raise NegationUnsupportedError, - "`#{expression}(...).#{method} receive` is not supported since it " + - "doesn't really make sense. What would it even mean?" + def self.disallow_negation(method_name) + define_method(method_name) do |matcher, *args| + raise_negation_unsupported(method_name, matcher) end end private + def raise_unsupported_matcher(method_name, matcher) + raise UnsupportedMatcherError, + "only the `receive` or `receive_messages` matchers are supported " + + "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" + end + + def raise_negation_unsupported(method_name, matcher) + raise NegationUnsupportedError, + "`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " + + "doesn't really make sense. What would it even mean?" + end + def expression self.class::EXPRESSION end From 8752218ca79022d79acac8474904b4ffceecf302 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 19 Aug 2013 11:34:34 +0100 Subject: [PATCH 0216/1179] preventing negative usage of receive_messages --- lib/rspec/mocks/matchers/receive_messages.rb | 7 +++++ lib/rspec/mocks/targets.rb | 30 ++++++++++++++----- .../mocks/matchers/receive_messages_spec.rb | 29 ++++++++++++++++++ 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index cabad836c..08b70a954 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -19,6 +19,13 @@ def setup_expectation(subject, &block) end alias matches? setup_expectation + def setup_negative_expectation(subject, &block) + raise NegationUnsupportedError, + "`expect(...).to_not receive_messages` is not supported since it " + + "doesn't really make sense. What would it even mean?" + end + alias does_not_match? setup_negative_expectation + def setup_allowance(subject) each_message_on( proxy_on(subject) ) do |host, message, return_value| host.add_simple_stub(message, return_value) diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 75ad4da19..9c949d457 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -8,14 +8,24 @@ def initialize(target) @target = target end - def self.delegate_to(matcher_method, options = {}) - method_name = options.fetch(:from) { :to } - define_method(method_name) do |matcher, &block| + def self.delegate_to(matcher_method) + define_method(:to) do |matcher, &block| unless Matchers::Receive === matcher || Matchers::ReceiveMessages === matcher raise_unsupported_matcher(:to, matcher) end + define_matcher(matcher, matcher_method, &block) + end + end - matcher.__send__(matcher_method, @target, &block) + def self.delegate_not_to(matcher_method, options = {}) + method_name = options.fetch(:from) + define_method(method_name) do |matcher, &block| + case matcher + when Matchers::Receive then define_matcher(matcher, matcher_method, &block) + when Matchers::ReceiveMessages then raise_negation_unsupported(method_name, matcher) + else + raise_unsupported_matcher(method_name, matcher) + end end end @@ -27,6 +37,10 @@ def self.disallow_negation(method_name) private + def define_matcher(matcher, name, &block) + matcher.__send__(name, @target, &block) + end + def raise_unsupported_matcher(method_name, matcher) raise UnsupportedMatcherError, "only the `receive` or `receive_messages` matchers are supported " + @@ -54,8 +68,8 @@ class AllowanceTarget < TargetBase class ExpectationTarget < TargetBase EXPRESSION = :expect delegate_to :setup_expectation - delegate_to :setup_negative_expectation, :from => :not_to - delegate_to :setup_negative_expectation, :from => :to_not + delegate_not_to :setup_negative_expectation, :from => :not_to + delegate_not_to :setup_negative_expectation, :from => :to_not end class AnyInstanceAllowanceTarget < TargetBase @@ -68,8 +82,8 @@ class AnyInstanceAllowanceTarget < TargetBase class AnyInstanceExpectationTarget < TargetBase EXPRESSION = :expect_any_instance_of delegate_to :setup_any_instance_expectation - delegate_to :setup_any_instance_negative_expectation, :from => :not_to - delegate_to :setup_any_instance_negative_expectation, :from => :to_not + delegate_not_to :setup_any_instance_negative_expectation, :from => :not_to + delegate_not_to :setup_any_instance_negative_expectation, :from => :to_not end end end diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index cadea4e5f..fc1c75554 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -87,5 +87,34 @@ module Mocks }.to raise_error "Implementation blocks aren't supported with `receive_messages`" end end + + describe "negative expectation failure" do + let(:obj) { Object.new } + + specify "allow(...).to_not receive_messages(:a => 1, :b => 2)" do + expect { allow(obj).to_not receive_messages(:a => 1, :b => 2) }.to( + raise_error "`allow(...).to_not receive_messages` is not supported "+ + "since it doesn't really make sense. What would it even mean?" + ) + end + specify "allow_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do + expect { allow_any_instance_of(obj).to_not receive_messages(:a => 1, :b => 2) }.to( + raise_error "`allow_any_instance_of(...).to_not receive_messages` is not supported "+ + "since it doesn't really make sense. What would it even mean?" + ) + end + specify "expect(...).to_not receive_messages(:a => 1, :b => 2)" do + expect { expect(obj).to_not receive_messages(:a => 1, :b => 2) }.to( + raise_error "`expect(...).to_not receive_messages` is not supported "+ + "since it doesn't really make sense. What would it even mean?" + ) + end + specify "expect_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do + expect { expect_any_instance_of(obj).to_not receive_messages(:a => 1, :b => 2) }.to( + raise_error "`expect_any_instance_of(...).to_not receive_messages` is not supported "+ + "since it doesn't really make sense. What would it even mean?" + ) + end + end end end From f9caf99924758dee268b91a1039aec4e4701b107 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 9 Sep 2013 14:20:48 +1000 Subject: [PATCH 0217/1179] refactor error handling --- lib/rspec/mocks/message_expectation.rb | 28 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index dcce79ff2..e1934ba4c 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -26,12 +26,11 @@ def called_max_times? end def verify_messages_received - unless @received - @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil) + BacktrackRestore.with(@backtrace_line) do + unless @received + @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil) + end end - rescue RSpec::Mocks::MockExpectationError => error - error.backtrace.insert(0, @backtrace_line) - Kernel::raise error end end @@ -265,10 +264,9 @@ def matches_name_but_not_args(message, *args) # @private def verify_messages_received - generate_error unless expected_messages_received? || failed_fast? - rescue RSpec::Mocks::MockExpectationError => error - error.backtrace.insert(0, @expected_from) - Kernel::raise error + BacktrackRestore.with(@expected_from) do + generate_error unless expected_messages_received? || failed_fast? + end end # @private @@ -605,5 +603,17 @@ def cannot_modify_further_error "to call the original implementation, and cannot be modified further." end end + + # Insert original locations into stacktraces + # @api private + class BacktrackRestore + def self.with(location) + yield + rescue RSpec::Mocks::MockExpectationError => error + error.backtrace.insert(0, location) + Kernel::raise error + end + end + end end From fd0f91ca5ab0c024ee06cb4c0eec5570c77fa557 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 9 Sep 2013 15:15:39 +1000 Subject: [PATCH 0218/1179] wording change for docs --- lib/rspec/mocks/syntax.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index f4948038e..a5f7a8952 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -331,8 +331,9 @@ def self.default_should_syntax_host # @method receive_messages # Shorthand syntax used to setup message(s), and their return value(s), # that you expect or allow an object to receive. The method takes a hash - # of messages and their respective return values. Unlike `receive`, block - # implementations are not supported, neither is the fluent interface. + # of messages and their respective return values. Unlike with `receive`, + # you cannot apply further customizations using a block or the fluent + # interface. # # @example # From d46944e62ba3ba1feb5b71ff9768b2a434997792 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 9 Sep 2013 15:26:14 +1000 Subject: [PATCH 0219/1179] ws --- spec/rspec/mocks/matchers/receive_messages_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index fc1c75554..5bc8cfb58 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -47,7 +47,6 @@ module Mocks failure end - it "sets up multiple expectations" do expect(obj).to receive_messages(:a => 1, :b => 2) obj.a From 2586e720a0fe805285130f66244b81c426e18808 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 9 Sep 2013 15:27:46 +1000 Subject: [PATCH 0220/1179] specify -> example and spacing --- spec/rspec/mocks/matchers/receive_messages_spec.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 5bc8cfb58..e8c640886 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -90,25 +90,28 @@ module Mocks describe "negative expectation failure" do let(:obj) { Object.new } - specify "allow(...).to_not receive_messages(:a => 1, :b => 2)" do + example "allow(...).to_not receive_messages(:a => 1, :b => 2)" do expect { allow(obj).to_not receive_messages(:a => 1, :b => 2) }.to( raise_error "`allow(...).to_not receive_messages` is not supported "+ "since it doesn't really make sense. What would it even mean?" ) end - specify "allow_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do + + example "allow_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do expect { allow_any_instance_of(obj).to_not receive_messages(:a => 1, :b => 2) }.to( raise_error "`allow_any_instance_of(...).to_not receive_messages` is not supported "+ "since it doesn't really make sense. What would it even mean?" ) end - specify "expect(...).to_not receive_messages(:a => 1, :b => 2)" do + + example "expect(...).to_not receive_messages(:a => 1, :b => 2)" do expect { expect(obj).to_not receive_messages(:a => 1, :b => 2) }.to( raise_error "`expect(...).to_not receive_messages` is not supported "+ "since it doesn't really make sense. What would it even mean?" ) end - specify "expect_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do + + example "expect_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do expect { expect_any_instance_of(obj).to_not receive_messages(:a => 1, :b => 2) }.to( raise_error "`expect_any_instance_of(...).to_not receive_messages` is not supported "+ "since it doesn't really make sense. What would it even mean?" From 8db5cbea5f719d25540ea56aaaf9c170b13640f3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 9 Sep 2013 16:00:47 +1000 Subject: [PATCH 0221/1179] warn about do/end blocks --- lib/rspec/mocks/matchers/receive_messages.rb | 12 +++++- lib/rspec/mocks/syntax.rb | 5 ++- .../mocks/matchers/receive_messages_spec.rb | 40 +++++++++++-------- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index 08b70a954..8fad472cf 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -12,14 +12,15 @@ def name "receive_messages" end - def setup_expectation(subject, &block) + def setup_expectation(subject) + warn_about_block if block_given? each_message_on( proxy_on(subject) ) do |host, message, return_value| host.add_simple_expectation(message, return_value, @backtrace_line) end end alias matches? setup_expectation - def setup_negative_expectation(subject, &block) + def setup_negative_expectation(subject) raise NegationUnsupportedError, "`expect(...).to_not receive_messages` is not supported since it " + "doesn't really make sense. What would it even mean?" @@ -27,21 +28,28 @@ def setup_negative_expectation(subject, &block) alias does_not_match? setup_negative_expectation def setup_allowance(subject) + warn_about_block if block_given? each_message_on( proxy_on(subject) ) do |host, message, return_value| host.add_simple_stub(message, return_value) end end def setup_any_instance_expectation(subject) + warn_about_block if block_given? each_message_on( any_instance_of(subject) ) do |host, message, return_value| host.should_receive(message).and_return(return_value) end end def setup_any_instance_allowance(subject) + warn_about_block if block_given? any_instance_of(subject).stub(@message_return_value_hash) end + def warn_about_block + raise "Implementation blocks aren't supported with `receive_messages`" + end + private def proxy_on(subject) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index a5f7a8952..13c81285a 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -93,8 +93,9 @@ def receive(method_name, &block) end def receive_messages(message_return_value_hash) - raise "Implementation blocks aren't supported with `receive_messages`" if block_given? - Matchers::ReceiveMessages.new(message_return_value_hash) + matcher = Matchers::ReceiveMessages.new(message_return_value_hash) + matcher.warn_about_block if block_given? + matcher end def allow(target) diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index e8c640886..14459451d 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -2,6 +2,22 @@ module RSpec module Mocks + shared_examples_for "complains when given blocks" do + it "complains if a { } block is given" do + expect { + target.to receive_messages(:a => 1) { "implementation" } + }.to raise_error "Implementation blocks aren't supported with `receive_messages`" + end + + it "complains if a do; end; block is given" do + expect { + target.to receive_messages(:a => 1) do + "implementation" + end + }.to raise_error "Implementation blocks aren't supported with `receive_messages`" + end + end + describe "allow(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { double "Object" } @@ -11,10 +27,8 @@ module Mocks expect(obj.b).to eq 2 end - it "complains if a block is given" do - expect { - allow(obj).to receive_messages(:a => 1) { "implementation" } - }.to raise_error "Implementation blocks aren't supported with `receive_messages`" + it_behaves_like "complains when given blocks" do + let(:target) { allow(obj) } end end @@ -27,10 +41,8 @@ module Mocks expect(obj.b).to eq 2 end - it "complains if a block is given" do - expect { - allow_any_instance_of(Object).to receive_messages(:a => 1) { "implementation" } - }.to raise_error "Implementation blocks aren't supported with `receive_messages`" + it_behaves_like "complains when given blocks" do + let(:target) { allow_any_instance_of(Object) } end end @@ -64,10 +76,8 @@ module Mocks expect(expectation_error.backtrace[0]).to match /#{__FILE__}:#{line}/ end - it "complains if a block is given" do - expect { - expect(double).to receive_messages(:a => 1) { "implementation" } - }.to raise_error "Implementation blocks aren't supported with `receive_messages`" + it_behaves_like "complains when given blocks" do + let(:target) { expect(double) } end end @@ -80,10 +90,8 @@ module Mocks expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError end - it "complains if a block is given" do - expect { - expect_any_instance_of(Object).to receive_messages(:a => 1) { "implementation" } - }.to raise_error "Implementation blocks aren't supported with `receive_messages`" + it_behaves_like "complains when given blocks" do + let(:target) { expect_any_instance_of(Object) } end end From 4cdb8bcd411a09c906c8b2bc5019c20b7dc4c970 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 9 Sep 2013 16:03:26 +1000 Subject: [PATCH 0222/1179] rename extracted backtrace logic --- lib/rspec/mocks/message_expectation.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index e1934ba4c..b3d29fae7 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -26,7 +26,7 @@ def called_max_times? end def verify_messages_received - BacktrackRestore.with(@backtrace_line) do + InsertOntoBacktrace.line(@backtrace_line) do unless @received @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil) end @@ -264,7 +264,7 @@ def matches_name_but_not_args(message, *args) # @private def verify_messages_received - BacktrackRestore.with(@expected_from) do + InsertOntoBacktrace.line(@expected_from) do generate_error unless expected_messages_received? || failed_fast? end end @@ -606,8 +606,8 @@ def cannot_modify_further_error # Insert original locations into stacktraces # @api private - class BacktrackRestore - def self.with(location) + class InsertOntoBacktrace + def self.line(location) yield rescue RSpec::Mocks::MockExpectationError => error error.backtrace.insert(0, location) From c3bdeff0ddbc4c321a96925ac5b8b3580fa977b7 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 9 Sep 2013 17:07:10 +1000 Subject: [PATCH 0223/1179] handle resetting partial objects --- lib/rspec/mocks/proxy.rb | 12 +++++++ .../mocks/matchers/receive_messages_spec.rb | 34 ++++++++++++------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index f8141a05a..a932140ca 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -253,6 +253,18 @@ def method_handle_for(message) nil end + # @private + def add_simple_expectation(method_name, response, location) + method_double[method_name].configure_method + super + end + + # @private + def add_simple_stub(method_name, response) + method_double[method_name].configure_method + super + end + private def any_instance_class_recorder_observing_method?(klass, method_name) diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 14459451d..63e1e23ab 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -18,8 +18,21 @@ module Mocks end end + shared_examples_for "handles partially mocked objects correctly" do + let(:obj) { Struct.new(:a).new('original') } + + it "resets partially mocked objects correctly" do + target.to receive_messages(:a => 1, :b => 2) + + expect { + reset obj + }.to change { obj.a }.from(1).to("original") + end + end + describe "allow(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { double "Object" } + let(:target) { allow(obj) } it "allows the object to respond to multiple messages" do allow(obj).to receive_messages(:a => 1, :b => 2) @@ -27,13 +40,13 @@ module Mocks expect(obj.b).to eq 2 end - it_behaves_like "complains when given blocks" do - let(:target) { allow(obj) } - end + it_behaves_like "complains when given blocks" + it_behaves_like "handles partially mocked objects correctly" end describe "allow_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { Object.new } + let(:target) { allow_any_instance_of(Object) } it "allows the object to respond to multiple messages" do allow_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) @@ -41,13 +54,12 @@ module Mocks expect(obj.b).to eq 2 end - it_behaves_like "complains when given blocks" do - let(:target) { allow_any_instance_of(Object) } - end + it_behaves_like "complains when given blocks" end describe "expect(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { double "Object" } + let(:target) { expect(obj) } let(:expectation_error) do failure = nil @@ -76,13 +88,13 @@ module Mocks expect(expectation_error.backtrace[0]).to match /#{__FILE__}:#{line}/ end - it_behaves_like "complains when given blocks" do - let(:target) { expect(double) } - end + it_behaves_like "complains when given blocks" + it_behaves_like "handles partially mocked objects correctly" end describe "expect_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { Object.new } + let(:target) { expect_any_instance_of(Object) } it "sets up multiple expectations" do expect_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) @@ -90,9 +102,7 @@ module Mocks expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError end - it_behaves_like "complains when given blocks" do - let(:target) { expect_any_instance_of(Object) } - end + it_behaves_like "complains when given blocks" end describe "negative expectation failure" do From cf4d4c9d52490103a48232502958fdd7d37c04e4 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 Jul 2013 17:46:53 +0200 Subject: [PATCH 0224/1179] Add a warning when the should syntax is used. Signed-off-by: Sam Phippen --- lib/rspec/mocks/configuration.rb | 8 ++- lib/rspec/mocks/syntax.rb | 24 ++++++++ spec/rspec/mocks/configuration_spec.rb | 79 ++++++++++++++++++++++++++ spec/spec_helper.rb | 6 ++ 4 files changed, 115 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index dc8feced3..9e1d47c7d 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -81,13 +81,17 @@ def transfer_nested_constants? def transfer_nested_constants=(val) @transfer_nested_constants = val end + + def reset_syntaxes_to_default + self.syntax = [:should, :expect] + RSpec::Mocks::Syntax.warn_about_should! + end end def self.configuration @configuration ||= Configuration.new end - configuration.syntax = [:should, :expect] + configuration.reset_syntaxes_to_default end end - diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index a2c2b5d2c..3cd011fbb 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -4,24 +4,42 @@ module Mocks # Provides methods for enabling and disabling the available syntaxes # provided by rspec-mocks. module Syntax + def self.warn_about_should! + @warn_about_should = true + end + + def self.warn_unless_should_configured(method_name) + if @warn_about_should + RSpec.deprecate( + "Using #{method_name} from the old `:should` syntax without explicitly enabling the syntax.", + :replacement => "the new `:expect` syntax or explicitly enable `:should`" + ) + + @warn_about_should = false + end + end # @api private # Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc). def self.enable_should(syntax_host = default_should_syntax_host) + @warn_about_should = false return if should_enabled?(syntax_host) syntax_host.class_exec do def should_receive(message, opts={}, &block) + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) opts[:expected_from] ||= CallerFilter.first_non_rspec_line ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block) end def should_not_receive(message, &block) + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) opts = {:expected_from => CallerFilter.first_non_rspec_line} ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block).never end def stub(message_or_hash, opts={}, &block) + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) if ::Hash === message_or_hash message_or_hash.each {|message, value| stub(message).and_return value } else @@ -31,29 +49,35 @@ def stub(message_or_hash, opts={}, &block) end def unstub(message) + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks.space.proxy_for(self).remove_stub(message) end def stub_chain(*chain, &blk) + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks::StubChain.stub_chain_on(self, *chain, &blk) end def as_null_object + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) @_null_object = true ::RSpec::Mocks.proxy_for(self).as_null_object end def null_object? + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) defined?(@_null_object) end def received_message?(message, *args, &block) + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks.proxy_for(self).received_message?(message, *args, &block) end unless Class.respond_to? :any_instance Class.class_exec do def any_instance + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks.any_instance_recorder_for(self) end end diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 93d9a6e20..cf6177963 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -85,6 +85,11 @@ def sandboxed expect(configured_syntax).to eq([:should]) end + it "does not warn about the should syntax" do + RSpec.should_not_receive(:deprecate) + Object.new.should_not_receive(:bees) + end + it 'is a no-op when configured a second time' do Syntax.default_should_syntax_host.should_not_receive(:method_added) ::RSpec::Mocks::ExampleMethods.should_not_receive(:method_undefined) @@ -110,6 +115,68 @@ def sandboxed it 'reports that both syntaxes are enabled' do expect(configured_syntax).to eq([:should, :expect]) end + + it "does not warn about the should syntax" do + RSpec.should_not_receive(:deprecate) + Object.new.should_not_receive(:bees) + end + end + + context "by default" do + before do + configure_default_syntax + end + + let(:expected_arguments) { + [ + /Using.*without explicitly enabling/, + {:replacement=>"the new `:expect` syntax or explicitly enable `:should`"} + ] + } + + it "it warns about should once, regardless of how many times it is called" do + expect(RSpec).to receive(:deprecate).with(*expected_arguments) + o = Object.new + o2 = Object.new + o.should_receive(:bees) + o2.should_receive(:bees) + + o.bees + o2.bees + end + + it "warns about should not once, regardless of how many times it is called" do + expect(RSpec).to receive(:deprecate).with(*expected_arguments) + o = Object.new + o2 = Object.new + o.should_not_receive(:bees) + o2.should_not_receive(:bees) + end + + it "warns about stubbing once, regardless of how many times it is called" do + expect(RSpec).to receive(:deprecate).with(*expected_arguments) + o = Object.new + o2 = Object.new + + o.stub(:faces) + o2.stub(:faces) + end + + it "doesn't warn about stubbing after a reset and setting should" do + expect(RSpec).not_to receive(:deprecate) + RSpec::Mocks.configuration.reset_syntaxes_to_default + RSpec::Mocks.configuration.syntax = :should + o = Object.new + o2 = Object.new + o.stub(:faces) + o2.stub(:faces) + end + + it "includes the call site in the deprecation warning" do + obj = Object.new + expect_deprecation_with_call_site(__FILE__, __LINE__ + 1) + obj.stub(:faces) + end end end @@ -122,6 +189,10 @@ def configure_syntax(syntax) def configured_syntax RSpec::Mocks.configuration.syntax end + + def configure_default_syntax + RSpec::Mocks.configuration.reset_syntaxes_to_default + end end end @@ -142,6 +213,14 @@ def configured_syntax end end end + + def configure_default_syntax + RSpec.configure do |rspec| + rspec.mock_with :rspec do |c| + c.reset_syntaxes_to_default + end + end + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7b610be10..446664b9e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -79,3 +79,9 @@ def reset(object) RSpec::Mocks.instance_variable_set(:@configuration, orig_configuration) end end + +def expect_deprecation_with_call_site(file, line) + expect(RSpec.configuration.reporter).to receive(:deprecation) do |options| + expect(options[:call_site]).to include([file, line].join(':')) + end +end From f96edc3c4db2f06703d9e1d97f2507c25d1145e8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 9 Sep 2013 23:20:29 -0700 Subject: [PATCH 0225/1179] These methods are not part of the public API. --- lib/rspec/mocks/configuration.rb | 2 ++ lib/rspec/mocks/syntax.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 9e1d47c7d..8a5e2c710 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -82,6 +82,8 @@ def transfer_nested_constants=(val) @transfer_nested_constants = val end + # @api private + # Resets the configured syntax to the default. def reset_syntaxes_to_default self.syntax = [:should, :expect] RSpec::Mocks::Syntax.warn_about_should! diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 3cd011fbb..fa7f3c7b3 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -4,10 +4,12 @@ module Mocks # Provides methods for enabling and disabling the available syntaxes # provided by rspec-mocks. module Syntax + # @api private def self.warn_about_should! @warn_about_should = true end + # @api private def self.warn_unless_should_configured(method_name) if @warn_about_should RSpec.deprecate( From b34488c3755aa800145f0df6d087fe16083d8f0b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 9 Sep 2013 23:58:13 -0700 Subject: [PATCH 0226/1179] Add changelog entry for #339. [ci skip] --- Changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.md b/Changelog.md index 164a5fbd4..4127c74d4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,6 +31,12 @@ Enhancements: * Default value of `transfer_nested_constants` option for constant stubbing can be configured. (Xavier Shay) +Deprecations: + +* Using the old `:should` syntax without explicitly configuring it + is disabled. It will continue to work but will emit a deprecation + warning in RSpec 3 if you do not explicitly enable it. (Sam Phippen) + Bug Fixes: * Fix `and_call_original` to handle a complex edge case involving From fd78578d0d65a7917701c4410a0eb9089ee6636f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 11 Sep 2013 18:53:22 -0700 Subject: [PATCH 0227/1179] Improve deprecation warning. --- lib/rspec/mocks/syntax.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 26cbc4448..becef8b0c 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -13,7 +13,7 @@ def self.warn_about_should! def self.warn_unless_should_configured(method_name) if @warn_about_should RSpec.deprecate( - "Using #{method_name} from the old `:should` syntax without explicitly enabling the syntax.", + "Using `#{method_name}` from the old `:should` syntax without explicitly enabling the syntax", :replacement => "the new `:expect` syntax or explicitly enable `:should`" ) From 203f662bd8d63690d3e322b11d241d3869053a45 Mon Sep 17 00:00:00 2001 From: Nick DeLuca Date: Wed, 11 Sep 2013 22:03:52 -0500 Subject: [PATCH 0228/1179] When generating failure message, don't use description if nil or empty --- lib/rspec/mocks/error_generator.rb | 8 ++++- .../mocks/failing_argument_matchers_spec.rb | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index d325017fd..6722895c3 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -171,7 +171,13 @@ def format_args(*args) end def arg_list(*args) - args.collect {|arg| arg.respond_to?(:description) ? arg.description : arg.inspect}.join(", ") + args.collect {|arg| arg_has_valid_description(arg) ? arg.description : arg.inspect }.join(", ") + end + + def arg_has_valid_description(arg) + return false unless arg.respond_to?(:description) + + !arg.description.nil? && !arg.description.empty? end def format_received_args(*args) diff --git a/spec/rspec/mocks/failing_argument_matchers_spec.rb b/spec/rspec/mocks/failing_argument_matchers_spec.rb index c31415cb5..f88550b57 100644 --- a/spec/rspec/mocks/failing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/failing_argument_matchers_spec.rb @@ -119,6 +119,39 @@ def inspect @double.msg arg end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (3)\n got: (my_thing)") end + + it "fails with sensible message when arg#description is nil" do + arg = Class.new do + def description + end + + def inspect + "my_thing" + end + end.new + + expect do + @double.should_receive(:msg).with(arg) + @double.msg 3 + end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (my_thing)\n got: (3)") + end + + it "fails with sensible message when arg#description is blank" do + arg = Class.new do + def description + "" + end + + def inspect + "my_thing" + end + end.new + + expect do + @double.should_receive(:msg).with(arg) + @double.msg 3 + end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (my_thing)\n got: (3)") + end end end end From 323a7d57f51ba87478c403359517c1c406a76a3e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 11 Sep 2013 09:30:06 +1000 Subject: [PATCH 0229/1179] add tests showing how any_instance partial mocks are reset --- spec/rspec/mocks/any_instance_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index c298dead5..4e2be90e8 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -225,6 +225,24 @@ def private_method; :private_method_return_value; end end end + context "when partially mocking objects" do + let(:obj) { klass.new } + + it "resets partially mocked objects correctly" do + allow_any_instance_of(klass).to receive(:existing_method).and_return("stubbed value") + + # Simply resetting the proxy doesn't work + # what we need to have happen is + # ::RSpec::Mocks.any_instance_recorder_for(klass).stop_all_observation! + # but that is never invoked in :: + expect { + RSpec::Mocks.space.verify_all + }.to( + change { obj.existing_method }.from("stubbed value").to(:existing_method_return_value) + ) + end + end + context "core ruby objects" do it "works uniformly across *everything*" do Object.any_instance.stub(:foo).and_return(1) From 08c6637dd2774b6c0f1f8aefb23bff0871d25491 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 11 Sep 2013 11:30:52 +1000 Subject: [PATCH 0230/1179] add tests for resetting of `receive` method doubles --- spec/rspec/mocks/matchers/receive_spec.rb | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index f5590f062..83155c455 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -148,11 +148,36 @@ def receiver.method_missing(*a); end # a poor man's stub... end end + shared_examples_for "resets partial mocks cleanly" do + let(:klass) { Struct.new(:foo) } + let(:object) { klass.new :bar } + + it "removes the method double" do + target.to receive(:foo).and_return(:baz) + expect { reset object }.to change { object.foo }.from(:baz).to(:bar) + end + end + + shared_examples_for "resets partial mocks of any instance cleanly" do + let(:klass) { Struct.new(:foo) } + let(:object) { klass.new :bar } + + it "removes the method double" do + target.to receive(:foo).and_return(:baz) + expect { + ::RSpec::Mocks.space.verify_all + }.to change { object.foo }.from(:baz).to(:bar) + end + end + describe "allow(...).to receive" do include_examples "an expect syntax allowance" do let(:receiver) { double } let(:wrapped) { allow(receiver) } end + include_examples "resets partial mocks cleanly" do + let(:target) { allow(object) } + end end describe "allow(...).not_to receive" do @@ -167,6 +192,9 @@ def receiver.method_missing(*a); end # a poor man's stub... let(:wrapped) { allow_any_instance_of(klass) } let(:receiver) { klass.new } end + include_examples "resets partial mocks of any instance cleanly" do + let(:target) { allow_any_instance_of(klass) } + end end describe "allow_any_instance_of(...).not_to receive" do @@ -180,6 +208,9 @@ def receiver.method_missing(*a); end # a poor man's stub... let(:receiver) { double } let(:wrapped) { expect(receiver) } end + include_examples "resets partial mocks cleanly" do + let(:target) { expect(object) } + end end describe "expect_any_instance_of(...).to receive" do @@ -188,6 +219,9 @@ def receiver.method_missing(*a); end # a poor man's stub... let(:wrapped) { expect_any_instance_of(klass) } let(:receiver) { klass.new } end + include_examples "resets partial mocks of any instance cleanly" do + let(:target) { expect_any_instance_of(klass) } + end end describe "expect(...).not_to receive" do From 538f5243e014e52f37910b6199ee51847ac4b9f5 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Tue, 24 Sep 2013 20:11:24 -0700 Subject: [PATCH 0231/1179] Verifying null objects only respond to defined methods. * No additional documentation, since this is the least surprising behaviour. Fixes #392. --- Changelog.md | 2 ++ lib/rspec/mocks/verifying_double.rb | 12 ++++++++++++ lib/rspec/mocks/verifying_proxy.rb | 2 -- spec/rspec/mocks/verifying_double_spec.rb | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3c45e46e7..2a38e2935 100644 --- a/Changelog.md +++ b/Changelog.md @@ -26,6 +26,8 @@ Enhancements: * Yield the receiver to `any_instance` implementation blocks (Sam Phippen). * Provide `instance_double` and `class_double` to create verifying doubles, ported from `rspec-fire` (Xavier Shay). +* `as_null_object` on a verifying double only responds to defined methods + (Xavier Shay). * Improved performance of double creation, particularly those with many attributes. (Xavier Shay) * Default value of `transfer_nested_constants` option for constant stubbing can diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index c45643205..27c83c3b1 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -4,11 +4,22 @@ module RSpec module Mocks + module VerifyingDouble + def method_missing(message, *args, &block) + # Null object conditional is an optimization. If not a null object, + # validity of method expectations will have been checked at definition + # time. + __mock_proxy.ensure_implemented(message) if null_object? + super + end + end + # A mock providing a custom proxy that can verify the validity of any # method stubs or expectations against the public instance methods of the # given class. class InstanceVerifyingMock include TestDouble + include VerifyingDouble def initialize(doubled_module, *args) @doubled_module = doubled_module @@ -32,6 +43,7 @@ def __build_mock_proxy # constants to work. class ClassVerifyingMock < Module include TestDouble + include VerifyingDouble def initialize(doubled_module, *args) @doubled_module = doubled_module diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 4b884f5ac..f262b8dad 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -58,8 +58,6 @@ def method_double end end - protected - def ensure_implemented(method_name) @doubled_module.when_loaded do |original_module| unless original_module.__send__(@method_checker, method_name) diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index e6511dcf3..48a41de1e 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -107,6 +107,13 @@ def prevents(&block) o = instance_double(LoadedClass, :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) end + + it 'only allows defined methods for null objects' do + o = instance_double('LoadedClass').as_null_object + + expect(o.defined_instance_method).to eq(o) + prevents { o.undefined_method } + end end end @@ -186,6 +193,13 @@ def prevents(&block) expect(dbl1).to receive(:undefined_class_method) } end + + it 'only allows defined methods for null objects' do + o = class_double('LoadedClass').as_null_object + + expect(o.defined_class_method).to eq(o) + prevents { o.undefined_method } + end end end From 3f6328c127946408654d7c2057bab9c22b59d723 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 25 Sep 2013 08:02:03 -0700 Subject: [PATCH 0232/1179] Rename verifying mocks to doubles. This is consistent with naming elsewhere. --- lib/rspec/mocks/example_methods.rb | 8 ++++---- lib/rspec/mocks/verifying_double.rb | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index ddfbeddb5..640b486af 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -35,28 +35,28 @@ def double(*args) # @overload instance_double(doubled_class, stubs) # @param doubled_class [String, Class] # @param stubs [Hash] (optional) hash of message/return-value pairs - # @return InstanceVerifyingMock + # @return InstanceVerifyingDouble # # Constructs a test double against a specific class. If the given class # name has been loaded, only instance methods defined on the class are # allowed to be stubbed. In all other ways it behaves like a # [double](double). def instance_double(doubled_class, *args) - declare_verifying_double(InstanceVerifyingMock, doubled_class, *args) + declare_verifying_double(InstanceVerifyingDouble, doubled_class, *args) end # @overload class_double(doubled_class) # @overload class_double(doubled_class, stubs) # @param doubled_class [String, Module] # @param stubs [Hash] (optional) hash of message/return-value pairs - # @return ClassVerifyingMock + # @return ClassVerifyingDouble # # Constructs a test double against a specific class. If the given class # name has been loaded, only class methods defined on the class are # allowed to be stubbed. In all other ways it behaves like a # [double](double). def class_double(doubled_class, *args) - declare_verifying_double(ClassVerifyingMock, doubled_class, *args) + declare_verifying_double(ClassVerifyingDouble, doubled_class, *args) end # Disables warning messages about expectations being set on nil. diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 27c83c3b1..870c96dd8 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -17,7 +17,7 @@ def method_missing(message, *args, &block) # A mock providing a custom proxy that can verify the validity of any # method stubs or expectations against the public instance methods of the # given class. - class InstanceVerifyingMock + class InstanceVerifyingDouble include TestDouble include VerifyingDouble @@ -36,12 +36,12 @@ def __build_mock_proxy end end - # Similar to an InstanceVerifyingMock, except that it verifies against + # Similar to an InstanceVerifyingDouble, except that it verifies against # public methods of the given class (i.e. the "class methods"). # # Module needs to be in the inheritance chain for transferring nested # constants to work. - class ClassVerifyingMock < Module + class ClassVerifyingDouble < Module include TestDouble include VerifyingDouble From 7ef80f59a3db22bc0c3d11ebf851ac9f699ba819 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Tue, 1 Oct 2013 18:51:42 -0700 Subject: [PATCH 0233/1179] Blocks provided to `with` are used as implementation. It is confusing to have different behaviour depending on whether the block has arguments or not, and there are better ways to do this. This behaviour matches that of other message expectations. --- Changelog.md | 1 + lib/rspec/mocks/message_expectation.rb | 9 +++++++-- .../mocks/verifying_message_expecation.rb | 7 +++---- spec/rspec/mocks/block_return_value_spec.rb | 6 ++++++ .../mocks/failing_argument_matchers_spec.rb | 5 ++--- .../mocks/passing_argument_matchers_spec.rb | 20 ------------------- .../verifying_message_expecation_spec.rb | 12 +---------- 7 files changed, 20 insertions(+), 40 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2a38e2935..4c6a4c87d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ Breaking Changes for 3.0.0: * Make `at_least(0)` raise an error. (Sam Phippen) * Remove support for `require 'spec/mocks'` which had been kept in place for backwards compatibility with rspec 1 (Myron Marston). +* Blocks provided to `with` are always used as implementation (Xavier Shay). Enhancements: diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index b3d29fae7..5ed900214 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -353,8 +353,13 @@ def raise_out_of_order_error # cart.add(Book.new(:isbn => 1934356379)) # # => passes def with(*args, &block) - self.inner_implementation_action = block if block_given? unless args.empty? - @argument_list_matcher = ArgumentListMatcher.new(*args, &block) + if args.empty? + raise ArgumentError, + "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments." + end + + self.inner_implementation_action = block + @argument_list_matcher = ArgumentListMatcher.new(*args) self end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 018571a2c..065d1aced 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -26,14 +26,13 @@ def initialize(*args) # @override def with(*args, &block) unless ArgumentMatchers::AnyArgsMatcher === args.first - expected_arity = if block - block.arity - elsif ArgumentMatchers::NoArgsMatcher === args.first + expected_arity = if ArgumentMatchers::NoArgsMatcher === args.first 0 elsif args.length > 0 args.length else - raise ArgumentError, "No arguments nor block given." + # No arguments given, this will raise. + super end ensure_arity!(expected_arity) diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 92bfac369..6d69aeb34 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -31,6 +31,12 @@ obj.stub(:foo).with('baz') { 'bar' } expect(obj.foo('baz')).to eq('bar') end + + it "returns the value of executing the block with given argument" do + obj = Object.new + obj.stub(:foo).with('baz') {|x| 'bar' + x } + expect(obj.foo('baz')).to eq('barbaz') + end end %w[once twice ordered and_return].each do |method| diff --git a/spec/rspec/mocks/failing_argument_matchers_spec.rb b/spec/rspec/mocks/failing_argument_matchers_spec.rb index c31415cb5..0be882ef6 100644 --- a/spec/rspec/mocks/failing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/failing_argument_matchers_spec.rb @@ -97,11 +97,10 @@ module Mocks end.to raise_error(/array_including\(1,2,3\)/) end - it "fails with block matchers" do + it "fails with zero arguments" do expect do @double.should_receive(:msg).with {|arg| expect(arg).to eq :received } - @double.msg :no_msg_for_you - end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /expected: :received.*\s*.*got: :no_msg_for_you/) + end.to raise_error(ArgumentError, /must have at least one argument/) end it "fails with sensible message when args respond to #description" do diff --git a/spec/rspec/mocks/passing_argument_matchers_spec.rb b/spec/rspec/mocks/passing_argument_matchers_spec.rb index fe70c05dd..2f8dd7248 100644 --- a/spec/rspec/mocks/passing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/passing_argument_matchers_spec.rb @@ -89,26 +89,6 @@ module Mocks end end - context "handling block matchers" do - it "matches arguments against RSpec expectations" do - @double.should_receive(:random_call).with {|arg1, arg2, arr, *rest| - expect(arg1).to eq 5 - expect(arg2.length).to be >= 3 - expect(arg2.length).to be <= 10 - expect(arr.map {|i| i * 2}).to eq [2,4,6] - expect(rest).to eq [:fee, "fi", 4] - } - @double.random_call 5, "hello", [1,2,3], :fee, "fi", 4 - end - - it "does not eval the block as the return value" do - eval_count = 0 - @double.should_receive(:msg).with {|a| eval_count += 1} - @double.msg(:ignore) - expect(eval_count).to eq(1) - end - end - context "handling non-matcher arguments" do it "matches non special symbol (can be removed when deprecated symbols are removed)" do @double.should_receive(:random_call).with(:some_symbol) diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 39236f156..442d81ef9 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -55,21 +55,11 @@ module Mocks end end - describe 'when called with a block' do - it 'matches arity against the arity of the block' do - subject.method_finder = Proc.new { lambda {|_| } } - expect(error_generator).to receive(:raise_arity_error). - with(instance_of(ArityCalculator), 2) - - subject.with {|x, y| } - end - end - describe 'when called with no arguments and no block' do it 'raises' do expect { subject.with - }.to raise_error(ArgumentError, "No arguments nor block given.") + }.to raise_error(ArgumentError) end end end From b3305ee2e82957200b407b747fd757f9db36df31 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 1 Oct 2013 23:12:19 -0700 Subject: [PATCH 0234/1179] Don't define helper methods on `main`. This adds it to _every_ object, which we don't want. --- spec/spec_helper.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 446664b9e..1c84b7f3a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -30,6 +30,14 @@ def reset(object) end end +module DeprecationHelpers + def expect_deprecation_with_call_site(file, line) + expect(RSpec.configuration.reporter).to receive(:deprecation) do |options| + expect(options[:call_site]).to include([file, line].join(':')) + end + end +end + RSpec.configure do |config| config.mock_with :rspec config.color_enabled = true @@ -52,6 +60,7 @@ def reset(object) end config.include VerifyAndResetHelpers + config.include DeprecationHelpers end shared_context "with syntax" do |syntax| @@ -80,8 +89,3 @@ def reset(object) end end -def expect_deprecation_with_call_site(file, line) - expect(RSpec.configuration.reporter).to receive(:deprecation) do |options| - expect(options[:call_site]).to include([file, line].join(':')) - end -end From 07f96d4d65d283cd1a4aa66ba4527890f33025aa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 1 Oct 2013 23:31:06 -0700 Subject: [PATCH 0235/1179] Add changelog entry for #417. [ci skip] --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4c6a4c87d..b7c6327a2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -48,6 +48,10 @@ Bug Fixes: singleton class ancestors. (Marc-André Lafortune, Myron Marston) * Fix issue where unstubing methods on "any instances" would not remove stubs on existing instances (Jon Rowe) +* When generating an error message for unexpected arguments, + use `#inspect` rather than `#description` if `#description` + returns `nil` or `''` so that you still get a useful message. + (Nick DeLuca) ### 2.14.3 / 2013-08-08 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) From f8662adf7df615f005f5c88b84ebd651257dfc99 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 28 Jul 2013 20:28:06 +1000 Subject: [PATCH 0236/1179] deprecation and warning reworking --- lib/rspec/mocks/framework.rb | 1 + lib/rspec/mocks/warning.rb | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 lib/rspec/mocks/warning.rb diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index f6e92a330..37d66f9b0 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -4,6 +4,7 @@ require 'rspec/mocks/caller_filter' unless defined?(::RSpec::CallerFilter) require 'rspec/mocks/deprecation' +require 'rspec/mocks/warning' require 'rspec/mocks/instance_method_stasher' require 'rspec/mocks/method_double' require 'rspec/mocks/argument_matchers' diff --git a/lib/rspec/mocks/warning.rb b/lib/rspec/mocks/warning.rb new file mode 100644 index 000000000..335ceebfc --- /dev/null +++ b/lib/rspec/mocks/warning.rb @@ -0,0 +1,22 @@ +module RSpec + module Mocks + module Warning + # @private + # + # Used internally to print deprecation warnings + def warning(text, options={}) + warn_with "WARNING: #{text}.", options + end + + def warn_with(message, options = {}) + line = caller.find { |line| line !~ %r{/lib/rspec/(core|mocks|expectations|matchers|rails)/} } + message << " Use #{options[:replacement]} instead." if options[:replacement] + message << " Called from #{line}." + ::Kernel.warn message + end + end + end + + extend(Mocks::Warning) unless respond_to?(:warning) && respond_to?(:warn_with) +end + From 1712a90c9ef393bc7cff6c3a1e8a7e0f94106fc5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 28 Jul 2013 20:28:30 +1000 Subject: [PATCH 0237/1179] warn when overriding an implementation block --- lib/rspec/mocks/message_expectation.rb | 8 ++++++++ spec/rspec/mocks/and_call_original_spec.rb | 14 ++++++++++---- .../combining_implementation_instructions_spec.rb | 7 +++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 5ed900214..447f3609b 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -149,6 +149,9 @@ def and_call_original if RSpec::Mocks::TestDouble === @method_double.object @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original) else + if implementation.inner_action + RSpec.warning("You're overriding a previous implementation for this stub") + end @implementation = AndCallOriginalImplementation.new(@method_double.original_method) @yield_receiver_to_implementation_block = false end @@ -501,6 +504,7 @@ def initial_implementation_action=(action) end def inner_implementation_action=(action) + RSpec.warning("You're overriding a previous implementation for this stub") if implementation.inner_action implementation.inner_action = action if action end @@ -597,6 +601,10 @@ def present? true end + def inner_action + true + end + def call(*args, &block) @method.call(*args, &block) end diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 0cced0784..d81aec3ee 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -22,7 +22,7 @@ def self.new_instance let(:instance) { klass.new } it 'passes the received message through to the original method' do - instance.should_receive(:meth_1).and_call_original + allow(instance).to receive(:meth_1).and_call_original expect(instance.meth_1).to eq(:original) end @@ -33,19 +33,25 @@ def self.new_instance end it 'passes args and blocks through to the original method' do - instance.should_receive(:meth_2).and_call_original + allow(instance).to receive(:meth_2).and_call_original value = instance.meth_2(:submitted_arg) { |a, b| [a, b] } expect(value).to eq([:submitted_arg, :additional_yielded_arg]) end it 'errors when you pass through the wrong number of args' do - instance.stub(:meth_1).and_call_original - instance.stub(:meth_2).and_call_original + allow(instance).to receive(:meth_1).and_call_original + allow(instance).to receive(:meth_2).and_call_original expect { instance.meth_1 :a }.to raise_error ArgumentError expect { instance.meth_2 {} }.to raise_error ArgumentError expect { instance.meth_2(:a, :b) {} }.to raise_error ArgumentError end + it 'warns when you override an existing implementation' do + expect(RSpec).to receive(:warning).with /overriding a previous implementation/ + allow(instance).to receive(:meth_1) { true }.and_call_original + instance.meth_1 + end + context "for singleton methods" do it 'works' do def instance.foo; :bar; end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 7904268b9..aa06939c1 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -159,6 +159,7 @@ def verify_combined_implementation end it 'allows the inner implementation block to be overriden' do + allow(RSpec).to receive(:warning) dbl = double stubbed_double = dbl.stub(:foo) @@ -169,6 +170,11 @@ def verify_combined_implementation expect(dbl.foo(:arg)).to eq(:at_least_block) end + it 'warns when the inner implementation block is overriden' do + expect(RSpec).to receive(:warning).with /overriding a previous implementation/ + double.stub(:foo).with(:arg) { :with_block }.at_least(:once) { :at_least_block } + end + it 'can combine and_call_original, with, and_return' do obj = Struct.new(:value).new('original') obj.stub(:value).and_call_original @@ -178,6 +184,7 @@ def verify_combined_implementation end it 'raises an error if `and_call_original` is followed by any other instructions' do + allow(RSpec).to receive(:warning) dbl = [1, 2, 3] stubbed = dbl.stub(:size) stubbed.and_call_original From 659b039405bbb4df9532e60c3503ae066757bb76 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 2 Oct 2013 19:58:23 +1000 Subject: [PATCH 0238/1179] unify warning files and match core --- lib/rspec/mocks/deprecation.rb | 18 ----------- lib/rspec/mocks/framework.rb | 3 +- lib/rspec/mocks/warning.rb | 22 ------------- lib/rspec/mocks/warnings.rb | 37 ++++++++++++++++++++++ spec/rspec/mocks/and_call_original_spec.rb | 10 +++--- 5 files changed, 43 insertions(+), 47 deletions(-) delete mode 100644 lib/rspec/mocks/deprecation.rb delete mode 100644 lib/rspec/mocks/warning.rb create mode 100644 lib/rspec/mocks/warnings.rb diff --git a/lib/rspec/mocks/deprecation.rb b/lib/rspec/mocks/deprecation.rb deleted file mode 100644 index e56ec6425..000000000 --- a/lib/rspec/mocks/deprecation.rb +++ /dev/null @@ -1,18 +0,0 @@ -module RSpec - module Mocks - module Deprecation - # @private - # - # Used internally to print deprecation warnings - def deprecate(deprecated, options={}) - message = "DEPRECATION: #{deprecated} is deprecated." - message << " Use #{options[:replacement]} instead." if options[:replacement] - message << " Called from #{CallerFilter.first_non_rspec_line}." - warn message - end - end - end - - extend(Mocks::Deprecation) unless respond_to?(:deprecate) -end - diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 37d66f9b0..adeabb4cb 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -3,8 +3,7 @@ # object in the system. require 'rspec/mocks/caller_filter' unless defined?(::RSpec::CallerFilter) -require 'rspec/mocks/deprecation' -require 'rspec/mocks/warning' +require 'rspec/mocks/warnings' require 'rspec/mocks/instance_method_stasher' require 'rspec/mocks/method_double' require 'rspec/mocks/argument_matchers' diff --git a/lib/rspec/mocks/warning.rb b/lib/rspec/mocks/warning.rb deleted file mode 100644 index 335ceebfc..000000000 --- a/lib/rspec/mocks/warning.rb +++ /dev/null @@ -1,22 +0,0 @@ -module RSpec - module Mocks - module Warning - # @private - # - # Used internally to print deprecation warnings - def warning(text, options={}) - warn_with "WARNING: #{text}.", options - end - - def warn_with(message, options = {}) - line = caller.find { |line| line !~ %r{/lib/rspec/(core|mocks|expectations|matchers|rails)/} } - message << " Use #{options[:replacement]} instead." if options[:replacement] - message << " Called from #{line}." - ::Kernel.warn message - end - end - end - - extend(Mocks::Warning) unless respond_to?(:warning) && respond_to?(:warn_with) -end - diff --git a/lib/rspec/mocks/warnings.rb b/lib/rspec/mocks/warnings.rb new file mode 100644 index 000000000..d93cb27a5 --- /dev/null +++ b/lib/rspec/mocks/warnings.rb @@ -0,0 +1,37 @@ +module RSpec + + # We don't redefine the deprecation helpers + # when they already exist (defined by rspec-core etc) + unless respond_to?(:deprecate) + + # @private + # + # Used internally to print deprecation warnings + def self.deprecate(deprecated, options = {}) + warn_with "DEPRECATION: #{deprecated} is deprecated.", options + end + + end + + # We don't redefine the warnings helpers + # when they already exist (defined by rspec-core etc) + unless respond_to?(:warning) && respond_to?(:warn_with) + + # @private + # + # Used internally to print deprecation warnings + def self.warning(text, options={}) + warn_with "WARNING: #{text}.", options + end + + # @private + # + # Used internally to longer warnings + def self.warn_with(message, options = {}) + message << " Use #{options[:replacement]} instead." if options[:replacement] + message << " Called from #{CallerFilter.first_non_rspec_line}." + ::Kernel.warn message + end + + end +end diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index d81aec3ee..3e3857072 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -22,7 +22,7 @@ def self.new_instance let(:instance) { klass.new } it 'passes the received message through to the original method' do - allow(instance).to receive(:meth_1).and_call_original + expect(instance).to receive(:meth_1).and_call_original expect(instance.meth_1).to eq(:original) end @@ -33,14 +33,14 @@ def self.new_instance end it 'passes args and blocks through to the original method' do - allow(instance).to receive(:meth_2).and_call_original + expect(instance).to receive(:meth_2).and_call_original value = instance.meth_2(:submitted_arg) { |a, b| [a, b] } expect(value).to eq([:submitted_arg, :additional_yielded_arg]) end it 'errors when you pass through the wrong number of args' do - allow(instance).to receive(:meth_1).and_call_original - allow(instance).to receive(:meth_2).and_call_original + expect(instance).to receive(:meth_1).and_call_original + expect(instance).to receive(:meth_2).twice.and_call_original expect { instance.meth_1 :a }.to raise_error ArgumentError expect { instance.meth_2 {} }.to raise_error ArgumentError expect { instance.meth_2(:a, :b) {} }.to raise_error ArgumentError @@ -48,7 +48,7 @@ def self.new_instance it 'warns when you override an existing implementation' do expect(RSpec).to receive(:warning).with /overriding a previous implementation/ - allow(instance).to receive(:meth_1) { true }.and_call_original + expect(instance).to receive(:meth_1) { true }.and_call_original instance.meth_1 end From 9d6bacf5323c2a6dc7276a80a30623d572398673 Mon Sep 17 00:00:00 2001 From: Ben Moss Date: Sat, 5 Oct 2013 17:19:13 -0400 Subject: [PATCH 0239/1179] Update cukes to new syntax --- features/argument_matchers/explicit.feature | 10 ++++----- .../general_matchers.feature | 20 ++++++++--------- .../argument_matchers/type_matchers.feature | 6 ++--- .../allow_any_instance_of.feature | 2 +- .../message_expectations/any_instance.feature | 2 +- .../call_original.feature | 4 ++-- .../expect_message_using_expect.feature | 4 ++-- ...xpect_message_using_should_receive.feature | 4 ++-- .../receive_counts.feature | 14 ++++++------ ...arn_when_expectation_is_set_on_nil.feature | 6 ++--- features/method_stubs/any_instance.feature | 22 +++++++++---------- features/method_stubs/as_null_object.feature | 8 +++---- .../simple_return_value_with_stub.feature | 14 ++++++------ features/method_stubs/stub_chain.feature | 6 ++--- .../method_stubs/stub_implementation.feature | 4 ++-- features/method_stubs/to_ary.feature | 4 ++-- .../hiding_defined_constant.feature | 4 ++-- .../stub_defined_constant.feature | 10 ++++----- .../stub_undefined_constant.feature | 12 +++++----- features/outside_rspec/standalone.feature | 2 +- .../spies/spy_partial_mock_method.feature | 4 ++-- features/spies/spy_pure_mock_method.feature | 8 +++---- features/spies/spy_unstubbed_method.feature | 2 +- 23 files changed, 86 insertions(+), 86 deletions(-) diff --git a/features/argument_matchers/explicit.feature b/features/argument_matchers/explicit.feature index 671ea6109..2c6d195d1 100644 --- a/features/argument_matchers/explicit.feature +++ b/features/argument_matchers/explicit.feature @@ -21,7 +21,7 @@ Feature: explicit arguments it "works on doubles and expectations" do object = double('foo') - object.should_receive(:bar).with(:foo) + expect(object).to receive(:bar).with(:foo) object.bar(:foo) end @@ -43,17 +43,17 @@ Feature: explicit arguments "got this and that" end - object.foo(:this).should eq("got this") - object.foo(:this, :that).should eq("got this and that") + expect(object.foo(:this)).to eq("got this") + expect(object.foo(:this, :that)).to eq("got this and that") end it "works on mocks" do object = double('foo') - object.should_receive(:foo).with(:this, :that) + expect(object).to receive(:foo).with(:this, :that) object.foo(:this, :that) end end """ When I run `rspec stub_multiple_explicit_args_spec.rb` - Then the output should contain "2 examples, 0 failures" \ No newline at end of file + Then the output should contain "2 examples, 0 failures" diff --git a/features/argument_matchers/general_matchers.feature b/features/argument_matchers/general_matchers.feature index 76d845a4b..0522d0397 100644 --- a/features/argument_matchers/general_matchers.feature +++ b/features/argument_matchers/general_matchers.feature @@ -15,8 +15,8 @@ Feature: General matchers "anything" end - object.foo(1).should eq("anything") - object.foo(:that).should eq("anything") + expect(object.foo(1)).to eq("anything") + expect(object.foo(:that)).to eq("anything") end end """ @@ -33,9 +33,9 @@ Feature: General matchers "anything" end - object.foo(1).should eq("anything") - object.foo(:that).should eq("anything") - object.foo.should eq("anything") + expect(object.foo(1)).to eq("anything") + expect(object.foo(:that)).to eq("anything") + expect(object.foo).to eq("anything") end end """ @@ -55,8 +55,8 @@ Feature: General matchers "something" end - object.foo(:that).should eq("something") - object.foo.should eq("nothing") + expect(object.foo(:that)).to eq("something") + expect(object.foo).to eq("nothing") end end """ @@ -69,17 +69,17 @@ Feature: General matchers describe "stubbed no_args() args spec for expectations" do it "works for no args" do object = Object.new - object.should_receive(:foo).with(no_args) + expect(object).to receive(:foo).with(no_args) object.foo end it "fails for args" do object = Object.new - object.should_receive(:foo).with(no_args) + expect(object).to receive(:foo).with(no_args) object.foo(:bar) end end """ When I run `rspec stub_no_args_expectations_spec.rb` - Then the output should contain "2 examples, 1 failure" \ No newline at end of file + Then the output should contain "2 examples, 1 failure" diff --git a/features/argument_matchers/type_matchers.feature b/features/argument_matchers/type_matchers.feature index 965228608..46d3ab442 100644 --- a/features/argument_matchers/type_matchers.feature +++ b/features/argument_matchers/type_matchers.feature @@ -17,10 +17,10 @@ Feature: stub with argument constraints "string" end - object.foo("bar").should eq("string") - object.foo(:that).should eq("symbol") + expect(object.foo("bar")).to eq("string") + expect(object.foo(:that)).to eq("symbol") end end """ When I run `rspec stub_an_instance_of_args_spec.rb` - Then the output should contain "1 example, 0 failures" \ No newline at end of file + Then the output should contain "1 example, 0 failures" diff --git a/features/message_expectations/allow_any_instance_of.feature b/features/message_expectations/allow_any_instance_of.feature index 5bf42dda6..0227018ce 100644 --- a/features/message_expectations/allow_any_instance_of.feature +++ b/features/message_expectations/allow_any_instance_of.feature @@ -7,7 +7,7 @@ Feature: allow a message on any instance of a class Scenario: allowing a message on any instance of a class Given a file named "example_spec.rb" with: """ruby - describe "any_instance.should_receive" do + describe "any_instance.to receive" do before do allow_any_instance_of(Object).to receive(:foo).and_return(:return_value) end diff --git a/features/message_expectations/any_instance.feature b/features/message_expectations/any_instance.feature index 26d7eb3f0..eefe87aac 100644 --- a/features/message_expectations/any_instance.feature +++ b/features/message_expectations/any_instance.feature @@ -35,7 +35,7 @@ Feature: expect a message on any instance of a class Object.any_instance.should_receive(:foo).and_return(:return_value) o = Object.new - o.foo.should eq(:return_value) + expect(o.foo).to eq(:return_value) end end """ diff --git a/features/message_expectations/call_original.feature b/features/message_expectations/call_original.feature index 98759405a..8a9a7cf30 100644 --- a/features/message_expectations/call_original.feature +++ b/features/message_expectations/call_original.feature @@ -14,8 +14,8 @@ Feature: Calling the original method describe "and_call_original" do it "delegates the message to the original implementation" do - Addition.should_receive(:two_plus_two).and_call_original - Addition.two_plus_two.should eq(4) + expect(Addition).to receive(:two_plus_two).and_call_original + expect(Addition.two_plus_two).to eq(4) end end """ diff --git a/features/message_expectations/expect_message_using_expect.feature b/features/message_expectations/expect_message_using_expect.feature index 3a86ec6ab..2789bc68d 100644 --- a/features/message_expectations/expect_message_using_expect.feature +++ b/features/message_expectations/expect_message_using_expect.feature @@ -75,7 +75,7 @@ Feature: expect message using `expect` it "returns the specified value" do object = double("object") expect(object).to receive(:message) { :return_value } - object.message.should eq(:return_value) + expect(object.message).to eq(:return_value) end end @@ -83,7 +83,7 @@ Feature: expect message using `expect` it "returns the specified value" do object = double("object") expect(object).to receive(:message).and_return(:return_value) - object.message.should eq(:return_value) + expect(object.message).to eq(:return_value) end end end diff --git a/features/message_expectations/expect_message_using_should_receive.feature b/features/message_expectations/expect_message_using_should_receive.feature index 747f05536..6ff9aed83 100644 --- a/features/message_expectations/expect_message_using_should_receive.feature +++ b/features/message_expectations/expect_message_using_should_receive.feature @@ -84,7 +84,7 @@ Feature: expect message using `should_receive` it "returns the specified value" do object = double("object") object.should_receive(:message) { :return_value } - object.message.should eq(:return_value) + expect(object.message).to eq(:return_value) end end @@ -92,7 +92,7 @@ Feature: expect message using `should_receive` it "returns the specified value" do object = double("object") object.should_receive(:message).and_return(:return_value) - object.message.should eq(:return_value) + expect(object.message).to eq(:return_value) end end end diff --git a/features/message_expectations/receive_counts.feature b/features/message_expectations/receive_counts.feature index d49a656bc..f8573593b 100644 --- a/features/message_expectations/receive_counts.feature +++ b/features/message_expectations/receive_counts.feature @@ -18,7 +18,7 @@ Feature: receive counts account = Account.new account.logger = logger - logger.should_receive(:account_opened).once + expect(logger).to receive(:account_opened).once account.open end @@ -46,7 +46,7 @@ Feature: receive counts account = Account.new account.logger = logger - logger.should_receive(:account_opened).twice + expect(logger).to receive(:account_opened).twice account.open account.open @@ -75,7 +75,7 @@ Feature: receive counts account = Account.new account.logger = logger - logger.should_receive(:account_opened).exactly(3).times + expect(logger).to receive(:account_opened).exactly(3).times account.open account.open @@ -105,7 +105,7 @@ Feature: receive counts account = Account.new account.logger = logger - logger.should_receive(:account_opened).at_least(:once) + expect(logger).to receive(:account_opened).at_least(:once) account.open end @@ -134,7 +134,7 @@ Feature: receive counts account = Account.new account.logger = logger - logger.should_receive(:account_opened).at_least(3).times + expect(logger).to receive(:account_opened).at_least(3).times # Note that I am calling method under test 4 times # and I specified it to be called at least 3 times @@ -167,7 +167,7 @@ Feature: receive counts account = Account.new account.logger = logger - logger.should_receive(:account_opened).at_most(:once) + expect(logger).to receive(:account_opened).at_most(:once) account.open account.open @@ -197,7 +197,7 @@ Feature: receive counts account = Account.new account.logger = logger - logger.should_receive(:account_opened).at_most(2).times + expect(logger).to receive(:account_opened).at_most(2).times account.open account.open diff --git a/features/message_expectations/warn_when_expectation_is_set_on_nil.feature b/features/message_expectations/warn_when_expectation_is_set_on_nil.feature index d82747532..3e905ed9b 100644 --- a/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +++ b/features/message_expectations/warn_when_expectation_is_set_on_nil.feature @@ -6,7 +6,7 @@ Feature: warn when expectation is set on nil RSpec.configure {|c| c.mock_with :rspec} describe "something" do it "does something" do - @i_do_not_exist.should_receive(:foo) + expect(@i_do_not_exist).to receive(:foo) @i_do_not_exist.foo end end @@ -21,7 +21,7 @@ Feature: warn when expectation is set on nil describe "something" do it "does something" do allow_message_expectations_on_nil - nil.should_receive(:foo) + expect(nil).to receive(:foo) nil.foo end end @@ -40,7 +40,7 @@ Feature: warn when expectation is set on nil nil.foo end it "does something (bar)" do - nil.should_receive(:bar) + expect(nil).to receive(:bar) nil.bar end end diff --git a/features/method_stubs/any_instance.feature b/features/method_stubs/any_instance.feature index 65e84eede..ce3bd9602 100644 --- a/features/method_stubs/any_instance.feature +++ b/features/method_stubs/any_instance.feature @@ -18,7 +18,7 @@ Feature: stub on any instance of a class Object.any_instance.stub(:foo).and_return(:return_value) o = Object.new - o.foo.should eq(:return_value) + expect(o.foo).to eq(:return_value) end end """ @@ -34,8 +34,8 @@ Feature: stub on any instance of a class Object.any_instance.stub(:foo => 'foo', :bar => 'bar') o = Object.new - o.foo.should eq('foo') - o.bar.should eq('bar') + expect(o.foo).to eq('foo') + expect(o.bar).to eq('bar') end end end @@ -53,8 +53,8 @@ Feature: stub on any instance of a class Object.any_instance.stub(:foo).with(:param_three, :param_four).and_return(:result_two) o = Object.new - o.foo(:param_one, :param_two).should eq(:result_one) - o.foo(:param_three, :param_four).should eq(:result_two) + expect(o.foo(:param_one, :param_two)).to eq(:result_one) + expect(o.foo(:param_three, :param_four)).to eq(:result_two) end end end @@ -76,7 +76,7 @@ Feature: stub on any instance of a class Object.any_instance.stub(:foo) Object.any_instance.unstub(:foo) - Object.new.foo.should eq(:foo) + expect(Object.new.foo).to eq(:foo) end end """ @@ -94,11 +94,11 @@ Feature: stub on any instance of a class :foo end end - Object.any_instance.should_receive(:foo).and_return(:bar) + expect_any_instance_of(Object).to receive(:foo).and_return(:bar) Object.any_instance.stub(:foo) Object.any_instance.unstub(:foo) - Object.new.foo.should eq(:bar) + expect(Object.new.foo).to eq(:bar) end end end @@ -113,21 +113,21 @@ Feature: stub on any instance of a class context "given symbols representing methods" do it "returns the correct value" do Object.any_instance.stub_chain(:one, :two, :three).and_return(:four) - Object.new.one.two.three.should eq(:four) + expect(Object.new.one.two.three).to eq(:four) end end context "given a hash at the end" do it "returns the correct value" do Object.any_instance.stub_chain(:one, :two, :three => :four) - Object.new.one.two.three.should eq(:four) + expect(Object.new.one.two.three).to eq(:four) end end context "given a string of methods separated by dots" do it "returns the correct value" do Object.any_instance.stub_chain("one.two.three").and_return(:four) - Object.new.one.two.three.should eq(:four) + expect(Object.new.one.two.three).to eq(:four) end end end diff --git a/features/method_stubs/as_null_object.feature b/features/method_stubs/as_null_object.feature index afddf9e0c..9a4b7bdcd 100644 --- a/features/method_stubs/as_null_object.feature +++ b/features/method_stubs/as_null_object.feature @@ -13,7 +13,7 @@ Feature: as_null_object let(:null_object) { double('null object').as_null_object } it "responds to any method that is not defined" do - null_object.should respond_to(:an_undefined_method) + expect(null_object).to respond_to(:an_undefined_method) end it "allows explicit stubs using expect syntax" do @@ -23,16 +23,16 @@ Feature: as_null_object it "allows explicit stubs using should syntax" do null_object.stub(:foo) { "bar" } - null_object.foo.should eq("bar") + expect(null_object.foo).to eq("bar") end it "allows explicit expectations" do - null_object.should_receive(:something) + expect(null_object).to receive(:something) null_object.something end it "supports Array#flatten" do - [null_object].flatten.should eq([null_object]) + expect([null_object].flatten).to eq([null_object]) end end """ diff --git a/features/method_stubs/simple_return_value_with_stub.feature b/features/method_stubs/simple_return_value_with_stub.feature index c942aeaee..33e2918e6 100644 --- a/features/method_stubs/simple_return_value_with_stub.feature +++ b/features/method_stubs/simple_return_value_with_stub.feature @@ -12,7 +12,7 @@ Feature: stub with a simple return value it "returns nil" do collaborator.stub(:message) - collaborator.message.should be(nil) + expect(collaborator.message).to be(nil) end end """ @@ -27,7 +27,7 @@ Feature: stub with a simple return value it "returns the specified value" do collaborator = double("collaborator") collaborator.stub(:message) { :value } - collaborator.message.should eq(:value) + expect(collaborator.message).to eq(:value) end end @@ -35,7 +35,7 @@ Feature: stub with a simple return value it "returns the specified value" do collaborator = double("collaborator") collaborator.stub(:message).and_return(:value) - collaborator.message.should eq(:value) + expect(collaborator.message).to eq(:value) end end @@ -43,8 +43,8 @@ Feature: stub with a simple return value it "returns the specified value" do collaborator = double("collaborator") collaborator.stub(:message_1 => :value_1, :message_2 => :value_2) - collaborator.message_1.should eq(:value_1) - collaborator.message_2.should eq(:value_2) + expect(collaborator.message_1).to eq(:value_1) + expect(collaborator.message_2).to eq(:value_2) end end @@ -54,8 +54,8 @@ Feature: stub with a simple return value :message_1 => :value_1, :message_2 => :value_2 ) - collaborator.message_1.should eq(:value_1) - collaborator.message_2.should eq(:value_2) + expect(collaborator.message_1).to eq(:value_1) + expect(collaborator.message_2).to eq(:value_2) end end end diff --git a/features/method_stubs/stub_chain.feature b/features/method_stubs/stub_chain.feature index ba389bd07..68b0b7e42 100644 --- a/features/method_stubs/stub_chain.feature +++ b/features/method_stubs/stub_chain.feature @@ -28,21 +28,21 @@ Feature: stub a chain of methods context "given symbols representing methods" do it "returns the correct value" do subject.stub_chain(:one, :two, :three).and_return(:four) - subject.one.two.three.should eq(:four) + expect(subject.one.two.three).to eq(:four) end end context "given a hash at the end" do it "returns the correct value" do subject.stub_chain(:one, :two, :three => :four) - subject.one.two.three.should eq(:four) + expect(subject.one.two.three).to eq(:four) end end context "given a string of methods separated by dots" do it "returns the correct value" do subject.stub_chain("one.two.three").and_return(:four) - subject.one.two.three.should eq(:four) + expect(subject.one.two.three).to eq(:four) end end end diff --git a/features/method_stubs/stub_implementation.feature b/features/method_stubs/stub_implementation.feature index 581e43dcb..ebba88c54 100644 --- a/features/method_stubs/stub_implementation.feature +++ b/features/method_stubs/stub_implementation.feature @@ -39,8 +39,8 @@ Feature: stub with substitute implementation end end - object.foo(:this).should eq("got this") - object.foo(:that).should eq("got that") + expect(object.foo(:this)).to eq("got this") + expect(object.foo(:that)).to eq("got that") end end """ diff --git a/features/method_stubs/to_ary.feature b/features/method_stubs/to_ary.feature index 251fa5054..66f123ccc 100644 --- a/features/method_stubs/to_ary.feature +++ b/features/method_stubs/to_ary.feature @@ -19,12 +19,12 @@ Feature: double handling to_ary shared_examples "to_ary" do it "can be overridden with a stub" do obj.stub(:to_ary) { :non_nil_value } - obj.to_ary.should be(:non_nil_value) + expect(obj.to_ary).to be(:non_nil_value) end it "supports Array#flatten" do obj = double('foo') - [obj].flatten.should eq([obj]) + expect([obj].flatten).to eq([obj]) end end diff --git a/features/mutating_constants/hiding_defined_constant.feature b/features/mutating_constants/hiding_defined_constant.feature index 296221f8e..9748394a1 100644 --- a/features/mutating_constants/hiding_defined_constant.feature +++ b/features/mutating_constants/hiding_defined_constant.feature @@ -14,7 +14,7 @@ Feature: Hide Defined Constant end it "restores the hidden constant when the example completes" do - FOO.should eq(7) + expect(FOO).to eq(7) end end """ @@ -38,7 +38,7 @@ Feature: Hide Defined Constant end it "restores the hidden constant when the example completes" do - MyGem::SomeClass::FOO.should eq(7) + expect(MyGem::SomeClass::FOO).to eq(7) end end end diff --git a/features/mutating_constants/stub_defined_constant.feature b/features/mutating_constants/stub_defined_constant.feature index 8d1669745..935daccb7 100644 --- a/features/mutating_constants/stub_defined_constant.feature +++ b/features/mutating_constants/stub_defined_constant.feature @@ -12,11 +12,11 @@ Feature: Stub Defined Constant describe "stubbing FOO" do it "can stub FOO with a different value" do stub_const("FOO", 5) - FOO.should eq(5) + expect(FOO).to eq(5) end it "restores the stubbed constant when the example completes" do - FOO.should eq(7) + expect(FOO).to eq(7) end end """ @@ -36,7 +36,7 @@ Feature: Stub Defined Constant describe SomeClass do it "stubs the nested constant when it is fully qualified" do stub_const("MyGem::SomeClass::FOO", 5) - SomeClass::FOO.should eq(5) + expect(SomeClass::FOO).to eq(5) end end end @@ -64,12 +64,12 @@ Feature: Stub Defined Constant it "transfers nested constants when using :transfer_nested_constants => true" do stub_const("MyGem::SomeClass", fake_class, :transfer_nested_constants => true) - SomeClass::FOO.should eq(7) + expect(SomeClass::FOO).to eq(7) end it "can specify a list of nested constants to transfer" do stub_const("MyGem::SomeClass", fake_class, :transfer_nested_constants => [:FOO]) - SomeClass::FOO.should eq(7) + expect(SomeClass::FOO).to eq(7) end end end diff --git a/features/mutating_constants/stub_undefined_constant.feature b/features/mutating_constants/stub_undefined_constant.feature index 48bb509a4..bd5d13f40 100644 --- a/features/mutating_constants/stub_undefined_constant.feature +++ b/features/mutating_constants/stub_undefined_constant.feature @@ -11,7 +11,7 @@ Feature: Stub Undefined Constant describe "stubbing FOO" do it "can stub undefined constant FOO" do stub_const("FOO", 5) - FOO.should eq(5) + expect(FOO).to eq(5) end it "undefines the constant when the example completes" do @@ -33,15 +33,15 @@ Feature: Stub Undefined Constant module MyGem describe SomeClass do it "can stub an arbitrarily deep constant that is undefined" do - defined?(SomeClass::A).should be_falsey + expect(defined?(SomeClass::A)).to be_falsey stub_const("MyGem::SomeClass::A::B::C", 3) - SomeClass::A::B::C.should eq(3) - SomeClass::A.should be_a(Module) + expect(SomeClass::A::B::C).to eq(3) + expect(SomeClass::A).to be_a(Module) end it 'undefines the intermediary constants that were dynamically created' do - defined?(SomeClass).should be_truthy - defined?(SomeClass::A).should be_falsey + expect(defined?(SomeClass)).to be_truthy + expect(defined?(SomeClass::A)).to be_falsey end end end diff --git a/features/outside_rspec/standalone.feature b/features/outside_rspec/standalone.feature index b6bec41ef..c7daffb5a 100644 --- a/features/outside_rspec/standalone.feature +++ b/features/outside_rspec/standalone.feature @@ -22,7 +22,7 @@ Feature: standalone require "rspec/mocks/standalone" greeter = double("greeter") - greeter.should_receive(:say_hi) + expect(greeter).to receive(:say_hi) RSpec::Mocks.verify """ diff --git a/features/spies/spy_partial_mock_method.feature b/features/spies/spy_partial_mock_method.feature index 26ee6391e..63d7ec974 100644 --- a/features/spies/spy_partial_mock_method.feature +++ b/features/spies/spy_partial_mock_method.feature @@ -11,7 +11,7 @@ Feature: Spy on a stubbed method on a partial mock invitation = Object.new invitation.stub(:deliver => true) invitation.deliver - invitation.should have_received(:deliver) + expect(invitation).to have_received(:deliver) end end """ @@ -25,7 +25,7 @@ Feature: Spy on a stubbed method on a partial mock it "fails when the expectation is not met" do invitation = Object.new invitation.stub(:deliver => true) - invitation.should have_received(:deliver) + expect(invitation).to have_received(:deliver) end end """ diff --git a/features/spies/spy_pure_mock_method.feature b/features/spies/spy_pure_mock_method.feature index 1883f639c..b1bda01e6 100644 --- a/features/spies/spy_pure_mock_method.feature +++ b/features/spies/spy_pure_mock_method.feature @@ -10,7 +10,7 @@ Feature: Spy on a stubbed method on a pure mock it "passes when the expectation is met" do invitation = double('invitation', :deliver => true) invitation.deliver - invitation.should have_received(:deliver) + expect(invitation).to have_received(:deliver) end end """ @@ -24,7 +24,7 @@ Feature: Spy on a stubbed method on a pure mock it "passes when the expectation is met" do invitation = double('invitation', :deliver => true) 2.times { invitation.deliver(:expected, :arguments) } - invitation.should have_received(:deliver). + expect(invitation).to have_received(:deliver). with(:expected, :arguments). twice end @@ -39,7 +39,7 @@ Feature: Spy on a stubbed method on a pure mock describe "have_received" do it "fails when the expectation is not met" do invitation = double('invitation', :deliver => true) - invitation.should have_received(:deliver) + expect(invitation).to have_received(:deliver) end end """ @@ -54,7 +54,7 @@ Feature: Spy on a stubbed method on a pure mock it "fails when the arguments are different" do invitation = double('invitation', :deliver => true) invitation.deliver(:unexpected) - invitation.should have_received(:deliver).with(:expected, :arguments) + expect(invitation).to have_received(:deliver).with(:expected, :arguments) end end """ diff --git a/features/spies/spy_unstubbed_method.feature b/features/spies/spy_unstubbed_method.feature index 8246ff956..8a82c5a6c 100644 --- a/features/spies/spy_unstubbed_method.feature +++ b/features/spies/spy_unstubbed_method.feature @@ -10,7 +10,7 @@ Feature: Spy on an unstubbed method it "raises a helpful error for unstubbed methods" do object = Object.new object.object_id - object.should have_received(:object_id) + expect(object).to have_received(:object_id) end end """ From 09a13b46a132684c6c8ccc9c11ff6ef20766333c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 6 Oct 2013 21:16:17 +1100 Subject: [PATCH 0240/1179] allow matchers to be chained off do...end blocks --- lib/rspec/mocks/matchers/receive.rb | 1 + spec/rspec/mocks/matchers/receive_spec.rb | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 8b12663b2..f6e6810db 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -76,6 +76,7 @@ def setup_method_substitute(host, method, block, *args) @recorded_customizations.each do |customization| customization.playback_onto(expectation) end + expectation end class Customization diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 83155c455..a5b936cd7 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -32,6 +32,14 @@ def verify_all expect(receiver.foo).to eq(4) end + it 'allows chaining off a `do...end` block implementation to be provided' do + wrapped.to receive(:foo) do + 4 + end.and_return(6) + + expect(receiver.foo).to eq(6) + end + it 'allows a `{ ... }` block implementation to be provided' do wrapped.to receive(:foo) { 5 } expect(receiver.foo).to eq(5) From cd7261f3fb7a72e97b63df7482cffe9cf47d8b07 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 7 Oct 2013 12:12:27 +1100 Subject: [PATCH 0241/1179] changelog for #428 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index b7c6327a2..9048e95f9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -52,6 +52,8 @@ Bug Fixes: use `#inspect` rather than `#description` if `#description` returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +* Fix issue with receive(:message) do ... end precedence preventing + the usage of modifications (`and_return` etc) (Jon Rowe) ### 2.14.3 / 2013-08-08 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) From 87b70217896f9147d32d5437e5df89d3053ade10 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 11 Oct 2013 21:09:15 -0700 Subject: [PATCH 0242/1179] Update gemspec. [ci skip] --- rspec-mocks.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 3a0f0f673..2e1dec505 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -8,7 +8,7 @@ Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.license = "MIT" s.authors = ["Steven Baker", "David Chelimsky", "Myron Marston"] - s.email = "rspec-users@rubyforge.org" + s.email = "rspec@googlegroups.com" s.homepage = "http://github.com/rspec/rspec-mocks" s.summary = "rspec-mocks-#{RSpec::Mocks::Version::STRING}" s.description = "RSpec's 'test double' framework, with support for stubbing and mocking" From 6a69138529e252179e944c8d4f7084c46c7dad59 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sun, 13 Oct 2013 17:13:13 -0300 Subject: [PATCH 0243/1179] Removes deprecated attribute 'already_proxied_respond_to'. --- lib/rspec/mocks/proxy.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index a932140ca..2237d3deb 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -11,7 +11,6 @@ def initialize(object, name=nil, options={}) @expectation_ordering = RSpec::Mocks::space.expectation_ordering @messages_received = [] @options = options - @already_proxied_respond_to = false @null_object = false end @@ -36,16 +35,6 @@ def method_handle_for(message) nil end - # @private - def already_proxied_respond_to - @already_proxied_respond_to = true - end - - # @private - def already_proxied_respond_to? - @already_proxied_respond_to - end - # @private def add_message_expectation(location, method_name, opts={}, &block) meth_double = method_double[method_name] From 635a72a1edca900b0aa2fd06ec95fe62ce2753bf Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 15 Oct 2013 20:39:39 +0100 Subject: [PATCH 0244/1179] Warn when allowances/expectations are set on any instance recorders. --- lib/rspec/mocks/any_instance/recorder.rb | 2 +- lib/rspec/mocks/matchers/receive.rb | 15 +++++++++++++++ spec/rspec/mocks/matchers/receive_spec.rb | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 81a2df550..896039ffe 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -11,7 +11,7 @@ module AnyInstance # @see Chain class Recorder # @private - attr_reader :message_chains, :stubs + attr_reader :message_chains, :stubs, :klass def initialize(klass) @message_chains = MessageChains.new diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 8b12663b2..865f88246 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -19,6 +19,7 @@ def name end def setup_expectation(subject, &block) + warn_if_any_instance("expect", subject) setup_mock_proxy_method_substitute(subject, :add_message_expectation, block) end alias matches? setup_expectation @@ -28,11 +29,14 @@ def setup_negative_expectation(subject, &block) # where `and_return` is meant to raise an error @recorded_customizations.unshift Customization.new(:never, [], nil) + warn_if_any_instance("expect", subject) + setup_expectation(subject, &block) end alias does_not_match? setup_negative_expectation def setup_allowance(subject, &block) + warn_if_any_instance("allow", subject) setup_mock_proxy_method_substitute(subject, :add_stub, block) end @@ -59,6 +63,17 @@ def setup_any_instance_allowance(subject, &block) private + def warn_if_any_instance(expression, subject) + if AnyInstance::Recorder === subject + RSpec.warning( + "`#{expression}(#{subject.klass}.any_instance).to` " << + "is probably not what you meant, it does not stub on " << + "any instance of `#{subject.klass}`. " << + "Use `#{expression}_any_instance_of(#{subject.klass}).to` instead." + ) + end + end + def setup_mock_proxy_method_substitute(subject, method, block) proxy = ::RSpec::Mocks.proxy_for(subject) setup_method_substitute(proxy, method, block, @backtrace_line) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index f5590f062..e5d51faea 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -9,6 +9,21 @@ def verify_all ::RSpec::Mocks.space.verify_all end + describe "expectations/allowances on any instance recorders" do + include_context "with syntax", [:expect, :should] + + it "warns about allow(Klass.any_instance).to receive..." do + expect(RSpec).to receive(:warning).with(/allow.*any_instance.*is probably not what you meant.*allow_any_instance_of.*instead/) + allow(Object.any_instance).to receive(:foo) + end + + it "warns about expect(Klass.any_instance).to receive..." do + expect(RSpec).to receive(:warning).with(/expect.*any_instance.*is probably not what you meant.*expect_any_instance_of.*instead/) + expect(Object.any_instance).to receive(:foo) + Object.any_instance.foo + end + end + shared_examples_for "a receive matcher" do |*options| it 'allows the caller to configure how the subject responds' do wrapped.to receive(:foo).and_return(5) From f3709305b087e76268e86a278b65c6ad5d954b47 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 15 Oct 2013 21:08:48 +0100 Subject: [PATCH 0245/1179] Add a changelog entry for #433 [ci skip] --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3c45e46e7..23e7ffd0c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -32,6 +32,9 @@ Enhancements: be configured. (Xavier Shay) * Messages can be allowed or expected on in bulk via `receive_messages(:message => :value)` (Jon Rowe) +* `allow(Klass.any_instance)` and `expect(Klass.any_instance)` now print a + warning. This is usually a mistake, and users usually want + `allow_any_instance_of` or `expect_any_instance_of` instead. (Sam Phippen) Deprecations: From 0199c03c55270628b65faa564cc2185b8f41fc05 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 15 Oct 2013 23:27:35 -0700 Subject: [PATCH 0246/1179] Add 2.14.4 release notes. [ci skip] --- Changelog.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index a5a222346..61d18e160 100644 --- a/Changelog.md +++ b/Changelog.md @@ -49,12 +49,18 @@ Bug Fixes: * Fix `and_call_original` to handle a complex edge case involving singleton class ancestors. (Marc-André Lafortune, Myron Marston) -* Fix issue where unstubing methods on "any instances" would not - remove stubs on existing instances (Jon Rowe) * When generating an error message for unexpected arguments, use `#inspect` rather than `#description` if `#description` returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) + +### 2.14.4 / 2013-10-15 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.3...v2.14.4) + +Bug Fixes: + +* Fix issue where unstubing methods on "any instances" would not + remove stubs on existing instances (Jon Rowe) * Fix issue with receive(:message) do ... end precedence preventing the usage of modifications (`and_return` etc) (Jon Rowe) From 5b114e29c0dc5f866eae1467c44337244b74f502 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 16 Oct 2013 11:08:12 +0100 Subject: [PATCH 0247/1179] Add specs on call sites of allow/expect(Klass.any_instance). --- spec/rspec/mocks/matchers/receive_spec.rb | 12 ++++++++++++ spec/spec_helper.rb | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index cc7d4eb80..6c6d4bcb5 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -11,17 +11,29 @@ def verify_all describe "expectations/allowances on any instance recorders" do include_context "with syntax", [:expect, :should] + include DeprecationHelpers it "warns about allow(Klass.any_instance).to receive..." do expect(RSpec).to receive(:warning).with(/allow.*any_instance.*is probably not what you meant.*allow_any_instance_of.*instead/) allow(Object.any_instance).to receive(:foo) end + it "includes the correct call site in the allow warning" do + expect_warning_with_call_site(__FILE__, __LINE__+1) + allow(Object.any_instance).to receive(:foo) + end + it "warns about expect(Klass.any_instance).to receive..." do expect(RSpec).to receive(:warning).with(/expect.*any_instance.*is probably not what you meant.*expect_any_instance_of.*instead/) expect(Object.any_instance).to receive(:foo) Object.any_instance.foo end + + it "includes the correct call site in the expect warning" do + expect_warning_with_call_site(__FILE__, __LINE__+1) + expect(Object.any_instance).to receive(:foo) + Object.any_instance.foo + end end shared_examples_for "a receive matcher" do |*options| diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1c84b7f3a..f14c8fa3a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -36,6 +36,10 @@ def expect_deprecation_with_call_site(file, line) expect(options[:call_site]).to include([file, line].join(':')) end end + + def expect_warning_with_call_site(file, line) + expect(Kernel).to receive(:warn).with(/Called from #{file}:#{line}/) + end end RSpec.configure do |config| From cc5312c054d531c38984aea7658482212f7977b6 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 16 Oct 2013 17:33:19 +0100 Subject: [PATCH 0248/1179] Don't use "stub" allow/expect(Class.any_instance) warnings Preferring "allow" or "expect" instead. --- lib/rspec/mocks/matchers/receive.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index e39dc2190..3419633bf 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -67,7 +67,7 @@ def warn_if_any_instance(expression, subject) if AnyInstance::Recorder === subject RSpec.warning( "`#{expression}(#{subject.klass}.any_instance).to` " << - "is probably not what you meant, it does not stub on " << + "is probably not what you meant, it does not #{expression} on " << "any instance of `#{subject.klass}`. " << "Use `#{expression}_any_instance_of(#{subject.klass}).to` instead." ) From 6556ed2ac8d3edf1dede460a5a7e3e61ba052e3b Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 16 Oct 2013 18:27:03 +0100 Subject: [PATCH 0249/1179] Clean up formatting and language on allow(O.any_instance) code. --- lib/rspec/mocks/matchers/receive.rb | 2 +- spec/rspec/mocks/matchers/receive_spec.rb | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 3419633bf..d616a8508 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -67,7 +67,7 @@ def warn_if_any_instance(expression, subject) if AnyInstance::Recorder === subject RSpec.warning( "`#{expression}(#{subject.klass}.any_instance).to` " << - "is probably not what you meant, it does not #{expression} on " << + "is probably not what you meant, it does not operate on " << "any instance of `#{subject.klass}`. " << "Use `#{expression}_any_instance_of(#{subject.klass}).to` instead." ) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 6c6d4bcb5..7432eaa27 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -11,7 +11,6 @@ def verify_all describe "expectations/allowances on any instance recorders" do include_context "with syntax", [:expect, :should] - include DeprecationHelpers it "warns about allow(Klass.any_instance).to receive..." do expect(RSpec).to receive(:warning).with(/allow.*any_instance.*is probably not what you meant.*allow_any_instance_of.*instead/) @@ -19,7 +18,7 @@ def verify_all end it "includes the correct call site in the allow warning" do - expect_warning_with_call_site(__FILE__, __LINE__+1) + expect_warning_with_call_site(__FILE__, __LINE__ + 1) allow(Object.any_instance).to receive(:foo) end @@ -30,7 +29,7 @@ def verify_all end it "includes the correct call site in the expect warning" do - expect_warning_with_call_site(__FILE__, __LINE__+1) + expect_warning_with_call_site(__FILE__, __LINE__ + 1) expect(Object.any_instance).to receive(:foo) Object.any_instance.foo end From 8131041047c0cb0a25e48fdc489b2ee0d0fac678 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 18 Oct 2013 22:58:36 +1100 Subject: [PATCH 0250/1179] setup rspec-support gem --- Gemfile | 2 +- rspec-mocks.gemspec | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 2345f9dda..b1031d537 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" gemspec -%w[rspec rspec-core rspec-expectations].each do |lib| +%w[rspec rspec-core rspec-expectations rspec-support].each do |lib| library_path = File.expand_path("../../#{lib}", __FILE__) if File.exist?(library_path) gem lib, :path => library_path diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 2e1dec505..af28e4d0e 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -23,6 +23,12 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.8.7' + if RSpec::Mocks::Version::STRING =~ /[a-zA-Z]+/ + s.add_runtime_dependency "rspec-support", "= #{RSpec::Mocks::Version::STRING}" + else + s.add_runtime_dependency "rspec-support", "~> #{RSpec::Mocks::Version::STRING.split('.')[0..1].concat(['0']).join('.')}" + end + s.add_development_dependency 'rake', '~> 10.0.0' s.add_development_dependency 'cucumber', '~> 1.1.9' s.add_development_dependency 'aruba', '~> 0.5' From 35065b4a17b08fc023a745d8bffd55db0acb06fd Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 18 Oct 2013 22:58:01 +1100 Subject: [PATCH 0251/1179] switch to support warnings / caller filter --- lib/rspec/mocks/caller_filter.rb | 55 -------------------------------- lib/rspec/mocks/framework.rb | 4 +-- lib/rspec/mocks/warnings.rb | 37 --------------------- spec/rspec/mocks_spec.rb | 6 ---- 4 files changed, 2 insertions(+), 100 deletions(-) delete mode 100644 lib/rspec/mocks/caller_filter.rb delete mode 100644 lib/rspec/mocks/warnings.rb diff --git a/lib/rspec/mocks/caller_filter.rb b/lib/rspec/mocks/caller_filter.rb deleted file mode 100644 index a3c29dd4e..000000000 --- a/lib/rspec/mocks/caller_filter.rb +++ /dev/null @@ -1,55 +0,0 @@ -module RSpec - # Consistent implementation for "cleaning" the caller method to strip out - # non-rspec lines. This enables errors to be reported at the call site in - # the code using the library, which is far more useful than the particular - # internal method that raised an error. - class CallerFilter - - RSPEC_LIBS = %w[ - core - mocks - expectations - matchers - rails - ] - - ADDITIONAL_TOP_LEVEL_FILES = %w[ autorun ] - - LIB_REGEX = %r{/lib/rspec/(#{(RSPEC_LIBS + ADDITIONAL_TOP_LEVEL_FILES).join('|')})(\.rb|/)} - - if RUBY_VERSION >= '2.0.0' - def self.first_non_rspec_line - # `caller` is an expensive method that scales linearly with the size of - # the stack. The performance hit for fetching it in chunks is small, - # and since the target line is probably near the top of the stack, the - # overall improvement of a chunked search like this is significant. - # - # See benchmarks/caller.rb for measurements. - - # Initial value here is mostly arbitrary, but is chosen to give good - # performance on the common case of creating a double. - increment = 5 - i = 1 - line = nil - - while !line - stack = caller(i, increment) - raise "No non-lib lines in stack" unless stack - - line = stack.find { |l| l !~ LIB_REGEX } - - i += increment - increment *= 2 # The choice of two here is arbitrary. - end - - line - end - else - # Earlier rubies do not support the two argument form of `caller`. This - # fallback is logically the same, but slower. - def self.first_non_rspec_line - caller.find { |line| line !~ LIB_REGEX } - end - end - end -end diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index adeabb4cb..d7b740b6f 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -2,8 +2,8 @@ # supports wrapping rspec's mocking functionality without invading every # object in the system. -require 'rspec/mocks/caller_filter' unless defined?(::RSpec::CallerFilter) -require 'rspec/mocks/warnings' +require 'rspec/support/caller_filter' +require 'rspec/support/warnings' require 'rspec/mocks/instance_method_stasher' require 'rspec/mocks/method_double' require 'rspec/mocks/argument_matchers' diff --git a/lib/rspec/mocks/warnings.rb b/lib/rspec/mocks/warnings.rb deleted file mode 100644 index d93cb27a5..000000000 --- a/lib/rspec/mocks/warnings.rb +++ /dev/null @@ -1,37 +0,0 @@ -module RSpec - - # We don't redefine the deprecation helpers - # when they already exist (defined by rspec-core etc) - unless respond_to?(:deprecate) - - # @private - # - # Used internally to print deprecation warnings - def self.deprecate(deprecated, options = {}) - warn_with "DEPRECATION: #{deprecated} is deprecated.", options - end - - end - - # We don't redefine the warnings helpers - # when they already exist (defined by rspec-core etc) - unless respond_to?(:warning) && respond_to?(:warn_with) - - # @private - # - # Used internally to print deprecation warnings - def self.warning(text, options={}) - warn_with "WARNING: #{text}.", options - end - - # @private - # - # Used internally to longer warnings - def self.warn_with(message, options = {}) - message << " Use #{options[:replacement]} instead." if options[:replacement] - message << " Called from #{CallerFilter.first_non_rspec_line}." - ::Kernel.warn message - end - - end -end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index d46c58eeb..65e0a94d8 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -63,10 +63,4 @@ def file_contents_for(lib, filename) File.read(file) end - it 'has an up-to-date caller_filter file' do - mocks = file_contents_for("mocks", "rspec/mocks/caller_filter.rb") - core = file_contents_for("core", "rspec/core/caller_filter.rb") - - expect(mocks).to eq(core) - end end From f32cb9552ce4998e1602e70d765e2cd86db7311d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 18 Oct 2013 23:13:45 +1100 Subject: [PATCH 0252/1179] truncate example (full support not available without core?) --- features/test_frameworks/test_unit.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/test_frameworks/test_unit.feature b/features/test_frameworks/test_unit.feature index 4897e7a80..702d6aa00 100644 --- a/features/test_frameworks/test_unit.feature +++ b/features/test_frameworks/test_unit.feature @@ -40,4 +40,4 @@ Feature: Test::Unit integration When I run `ruby rspec_mocks_test.rb` Then the output should contain "3 tests, 0 assertions, 0 failures, 1 errors" or "3 tests, 0 assertions, 1 failures, 0 errors" And the output should contain "expected: 0 times with any arguments" - And the output should contain "old_message is deprecated. Use message instead." + And the output should contain "old_message is deprecated" From 55fa2334c0a08e09563b68b0a89b1660162f253d Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Wed, 25 Sep 2013 22:38:43 -0400 Subject: [PATCH 0253/1179] Raises realistic errors if verified methods are called with incorrect arity Reifies the `MethodReference` concept; otherwise we would have to be passing three different parameters around to cover each possible case of a method being implemented versus defined, as well as code to retrieve the method object. [Closes #393] --- lib/rspec/mocks/method_double.rb | 8 ++- lib/rspec/mocks/method_reference.rb | 72 +++++++++++++++++++ lib/rspec/mocks/verifying_double.rb | 6 +- .../mocks/verifying_message_expecation.rb | 22 +++--- lib/rspec/mocks/verifying_proxy.rb | 59 +++++++++------ spec/rspec/mocks/verifying_double_spec.rb | 28 ++++++++ .../verifying_message_expecation_spec.rb | 14 ++-- 7 files changed, 164 insertions(+), 45 deletions(-) create mode 100644 lib/rspec/mocks/method_reference.rb diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 6343b5ab6..3944e964f 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -56,9 +56,10 @@ def define_proxy_method @original_method = @method_stasher.original_method || @proxy.method_handle_for(method_name) + method_double = self object_singleton_class.class_exec(method_name, visibility) do |method_name, visibility| define_method(method_name) do |*args, &block| - ::RSpec::Mocks.proxy_for(self).message_received method_name, *args, &block + method_double.proxy_method_invoked(self, *args, &block) end self.__send__ visibility, method_name end @@ -66,6 +67,11 @@ def define_proxy_method @method_is_proxied = true end + # @private + def proxy_method_invoked(obj, *args, &block) + ::RSpec::Mocks.proxy_for(obj).message_received method_name, *args, &block + end + # @private def restore_original_method return unless @method_is_proxied diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb new file mode 100644 index 000000000..9453c4394 --- /dev/null +++ b/lib/rspec/mocks/method_reference.rb @@ -0,0 +1,72 @@ +module RSpec + module Mocks + # Represents a method on a module that may or may not be defined. + # + # @private + class MethodReference + def initialize(module_reference, method_name) + @module_reference = module_reference + @method_name = method_name + end + + # A method is implemented if sending the message to the object will + # result in a reply. It might be dynamically implemented by + # `method_missing`. + def implemented? + @module_reference.when_loaded do |m| + method_implemented?(m) + end + end + + # A method is defined if the method is already defined on the object's + # class. In that case, we can create a method object for it and assert + # against metadata like arity. + def defined? + @module_reference.when_loaded do |m| + method_defined?(m) + end + end + + def when_defined(&block) + yield original_method if original_method + end + + private + def original_method + @module_reference.when_loaded do |m| + self.defined? && find_method(m) + end + end + end + + # @private + class InstanceMethodReference < MethodReference + def method_implemented?(m) + m.method_defined?(@method_name) + end + + def method_defined?(m) + m.method_defined?(@method_name) + end + + def find_method(m) + m.instance_method(@method_name) + end + end + + # @private + class ClassMethodReference < MethodReference + def method_implemented?(m) + m.respond_to?(@method_name) + end + + def method_defined?(m) + (class << m; self; end).method_defined?(@method_name) + end + + def find_method(m) + m.method(@method_name) + end + end + end +end diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 870c96dd8..19a23f296 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -30,8 +30,7 @@ def initialize(doubled_module, *args) def __build_mock_proxy VerifyingProxy.new(self, @doubled_module, - :method_defined?, - :instance_method + InstanceMethodReference ) end end @@ -54,8 +53,7 @@ def initialize(doubled_module, *args) def __build_mock_proxy VerifyingProxy.new(self, @doubled_module, - :respond_to?, - :method + ClassMethodReference ) end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 065d1aced..b98330701 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -12,15 +12,14 @@ class VerifyingMessageExpectation < MessageExpectation # method itself, since method look up is expensive and we only want to # do it if actually needed. # - # Conceptually the method finder makes more sense as a constructor + # Conceptually the method reference makes more sense as a constructor # argument since it should be immutable, but it is significantly more # straight forward to build the object in pieces so for now it stays as # an accessor. - attr_accessor :method_finder + attr_accessor :method_reference def initialize(*args) super - @method_finder = Proc.new { ArityCalculator::MethodNotLoaded } end # @override @@ -43,13 +42,16 @@ def with(*args, &block) private def ensure_arity!(actual) - calculator = ArityCalculator.new(method_finder.call(message)) - - unless calculator.within_range?(actual) - # Fail fast is required, otherwise the message expecation will fail - # as well ("expected method not called") and clobber this one. - @failed_fast = true - @error_generator.raise_arity_error(calculator, actual) + return if method_reference.nil? + + method_reference.when_defined do |method| + calculator = ArityCalculator.new(method) + unless calculator.within_range?(actual) + # Fail fast is required, otherwise the message expecation will fail + # as well ("expected method not called") and clobber this one. + @failed_fast = true + @error_generator.raise_arity_error(calculator, actual) + end end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index f262b8dad..51c07288f 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -1,4 +1,5 @@ require 'rspec/mocks/verifying_message_expecation' +require 'rspec/mocks/method_reference' module RSpec module Mocks @@ -18,12 +19,11 @@ module Mocks # # @api private class VerifyingProxy < Proxy - def initialize(object, name, method_checker, method_finder) + def initialize(object, name, method_reference_factory) super(object) - @object = object - @doubled_module = name - @method_checker = method_checker - @method_finder = method_finder + @object = object + @doubled_module = name + @method_reference_factory = method_reference_factory end def add_stub(location, method_name, opts={}, &implementation) @@ -46,42 +46,55 @@ def add_message_expectation(location, method_name, opts={}, &block) # class is loaded. def method_double @method_double ||= Hash.new do |h,k| - method_double = VerifyingMethodDouble.new(@object, k, self) - - @doubled_module.when_loaded do |original_module| - method_double.method_finder = lambda do |method_name| - original_module.__send__(@method_finder, method_name) - end - end + h[k] = VerifyingMethodDouble.new(@object, k, self, method_reference[k]) + end + end - h[k] = method_double + def method_reference + @method_reference ||= Hash.new do |h, k| + h[k] = @method_reference_factory.new(@doubled_module, k) end end def ensure_implemented(method_name) - @doubled_module.when_loaded do |original_module| - unless original_module.__send__(@method_checker, method_name) - @error_generator.raise_unimplemented_error( - @doubled_module, - method_name - ) - end + if @doubled_module.defined? && !method_reference[method_name].implemented? + @error_generator.raise_unimplemented_error( + @doubled_module, + method_name + ) end end end # @api private class VerifyingMethodDouble < MethodDouble - attr_accessor :method_finder + def initialize(object, method_name, proxy, method_reference) + super(object, method_name, proxy) + @method_reference = method_reference + end def message_expectation_class VerifyingMessageExpectation end def add_expectation(*arg) - super.tap {|x| x.method_finder = method_finder if method_finder } + super.tap { |x| x.method_reference = @method_reference } end - end + def proxy_method_invoked(obj, *args, &block) + ensure_arity!(args.length) + super + end + + private + def ensure_arity!(arity) + @method_reference.when_defined do |method| + calculator = ArityCalculator.new(method) + unless calculator.within_range?(arity) + raise ArgumentError, "wrong number of arguments (#{arity} for #{method.arity})" + end + end + end + end end end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 48a41de1e..61947c642 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -103,6 +103,13 @@ def prevents(&block) } end + it 'checks that stubbed methods are invoked with the correct arity' do + o = instance_double('LoadedClass', :defined_instance_method => 25) + expect { + o.defined_instance_method(:a) + }.to raise_error(ArgumentError) + end + it 'allows class to be specified by constant' do o = instance_double(LoadedClass, :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) @@ -158,6 +165,27 @@ def prevents(&block) prevents { o.should_receive(:defined_instance_method) } end + it 'checks that stubbed methods are invoked with the correct arity' do + o = class_double('LoadedClass', :defined_class_method => 1) + expect { + o.defined_class_method(:a) + }.to raise_error(ArgumentError) + end + + it 'allows dynamically defined class method stubs with arguments' do + o = class_double('LoadedClass') + allow(o).to receive(:dynamic_class_method).with(:a) { 1 } + + expect(o.dynamic_class_method(:a)).to eq(1) + end + + it 'allows dynamically defined class method mocks with arguments' do + o = class_double('LoadedClass') + expect(o).to receive(:dynamic_class_method).with(:a) + + o.dynamic_class_method(:a) + end + it 'allows dynamically defined class methods to be expected' do o = class_double('LoadedClass', :dynamic_class_method => 1) expect(o.dynamic_class_method).to eq(1) diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 442d81ef9..ba6841f67 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -4,8 +4,8 @@ module RSpec module Mocks describe VerifyingMessageExpectation do describe '#with' do - let(:method_tracer) { Object.new } let(:error_generator) { double.as_null_object } + let(:string_module_reference) { ModuleReference.new(String) } subject { null = double.as_null_object @@ -21,33 +21,33 @@ module Mocks describe 'when arity match fails' do it 'raises error' do - subject.method_finder = Proc.new { lambda {|_| } } + subject.method_reference = InstanceMethodReference.new(string_module_reference, :include?) expect(error_generator).to receive(:raise_arity_error). with(instance_of(ArityCalculator), 2) - subject.with(nil, nil) + subject.with("abc123", "xyz987") end end describe 'when called with arguments' do it 'matches arity against the number of arguments' do - subject.method_finder = Proc.new { lambda {|_| } } + subject.method_reference = InstanceMethodReference.new(string_module_reference, :include?) expect(error_generator).not_to receive(:raise_arity_error) - subject.with(nil) + subject.with("abc123") end end describe 'when called with any arguments matcher' do it 'does not try to match arity' do - subject.method_finder = Proc.new { raise } + subject.method_reference = InstanceMethodReference.new(string_module_reference, :include?) subject.with(any_args) end end describe 'when called with no arguments matcher' do it 'matches arity to 0' do - subject.method_finder = Proc.new { lambda {|_| } } + subject.method_reference = InstanceMethodReference.new(string_module_reference, :include?) expect(error_generator).to receive(:raise_arity_error). with(instance_of(ArityCalculator), 0) From b0e0c5fe492922860406400f92b05b1605392a2f Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 6 Oct 2013 22:10:20 -0400 Subject: [PATCH 0254/1179] Address some small code review comment suggestions --- lib/rspec/mocks/arity_calculator.rb | 5 ----- lib/rspec/mocks/method_double.rb | 6 ++++-- lib/rspec/mocks/method_reference.rb | 24 +++++++++++++---------- lib/rspec/mocks/verifying_proxy.rb | 3 ++- spec/rspec/mocks/arity_calculator_spec.rb | 8 -------- 5 files changed, 20 insertions(+), 26 deletions(-) diff --git a/lib/rspec/mocks/arity_calculator.rb b/lib/rspec/mocks/arity_calculator.rb index 7a53d8c3e..5f519a910 100644 --- a/lib/rspec/mocks/arity_calculator.rb +++ b/lib/rspec/mocks/arity_calculator.rb @@ -4,17 +4,12 @@ module Mocks # Figures out the valid arity range for a method. Surprisingly non-trivial. class ArityCalculator - # Default value for method if it not available. - MethodNotLoaded = Class.new - def initialize(method) @method = method end # @api private def within_range?(actual) - return true if method == MethodNotLoaded - min_arity <= actual && actual <= max_arity end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 3944e964f..d513062fa 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -56,8 +56,7 @@ def define_proxy_method @original_method = @method_stasher.original_method || @proxy.method_handle_for(method_name) - method_double = self - object_singleton_class.class_exec(method_name, visibility) do |method_name, visibility| + object_singleton_class.class_exec(self, method_name, visibility) do |method_double, method_name, visibility| define_method(method_name) do |*args, &block| method_double.proxy_method_invoked(self, *args, &block) end @@ -67,6 +66,9 @@ def define_proxy_method @method_is_proxied = true end + # The implementation of the proxied method. Subclasses may override this + # method to perform additional operations. + # # @private def proxy_method_invoked(obj, *args, &block) ::RSpec::Mocks.proxy_for(obj).message_received method_name, *args, &block diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 9453c4394..f05ec7b9e 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -9,8 +9,8 @@ def initialize(module_reference, method_name) @method_name = method_name end - # A method is implemented if sending the message to the object will - # result in a reply. It might be dynamically implemented by + # A method is implemented if sending the message does not result in + # a `NoMethodError`. It might be dynamically implemented by # `method_missing`. def implemented? @module_reference.when_loaded do |m| @@ -18,17 +18,18 @@ def implemented? end end - # A method is defined if the method is already defined on the object's - # class. In that case, we can create a method object for it and assert - # against metadata like arity. + # A method is defined if we are able to get a `Method` object for it. + # In that case, we can assert against metadata like the arity. def defined? @module_reference.when_loaded do |m| method_defined?(m) end end - def when_defined(&block) - yield original_method if original_method + def when_defined + if original = original_method + yield original + end end private @@ -45,9 +46,12 @@ def method_implemented?(m) m.method_defined?(@method_name) end - def method_defined?(m) - m.method_defined?(@method_name) - end + # Ideally, we'd use `respond_to?` for `method_implemented?` but we need a + # reference to an instance to do that and we don't have one. Note that + # we may get false negatives: if the method is implemented via + # `method_missing`, we'll return `false` even though it meets our + # definition of "implemented". However, it's the best we can do. + alias method_defined? method_implemented? def find_method(m) m.instance_method(@method_name) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 51c07288f..d4266c2bb 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -86,7 +86,8 @@ def proxy_method_invoked(obj, *args, &block) super end - private + private + def ensure_arity!(arity) @method_reference.when_defined do |method| calculator = ArityCalculator.new(method) diff --git a/spec/rspec/mocks/arity_calculator_spec.rb b/spec/rspec/mocks/arity_calculator_spec.rb index cf9f0b297..864c142f0 100644 --- a/spec/rspec/mocks/arity_calculator_spec.rb +++ b/spec/rspec/mocks/arity_calculator_spec.rb @@ -14,14 +14,6 @@ def description subject.range_description end - describe 'with unloaded method' do - let(:test_method) { ArityCalculator::MethodNotLoaded } - - it 'covers any range' do - expect(within_range?(123)).to eq(true) - end - end - describe 'with a method with arguments' do def arity_two(x, y); end From e0a9bcdb0ad025dfa4d91901c196fd98e5bd843c Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Mon, 14 Oct 2013 11:03:39 -0700 Subject: [PATCH 0255/1179] Locks down visibility of methods not intended to be used elsewhere --- lib/rspec/mocks/method_reference.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index f05ec7b9e..d14d2ff88 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -42,6 +42,7 @@ def original_method # @private class InstanceMethodReference < MethodReference + private def method_implemented?(m) m.method_defined?(@method_name) end @@ -60,6 +61,7 @@ def find_method(m) # @private class ClassMethodReference < MethodReference + private def method_implemented?(m) m.respond_to?(@method_name) end From 4e326b05ef9054f46aef9654ba3a6ba6ce949a26 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Mon, 14 Oct 2013 11:04:48 -0700 Subject: [PATCH 0256/1179] Changes object name slightly per code review suggestion --- lib/rspec/mocks/verifying_proxy.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index d4266c2bb..200898e60 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -19,11 +19,11 @@ module Mocks # # @api private class VerifyingProxy < Proxy - def initialize(object, name, method_reference_factory) + def initialize(object, name, method_reference_class) super(object) - @object = object - @doubled_module = name - @method_reference_factory = method_reference_factory + @object = object + @doubled_module = name + @method_reference_class = method_reference_class end def add_stub(location, method_name, opts={}, &implementation) @@ -52,7 +52,7 @@ def method_double def method_reference @method_reference ||= Hash.new do |h, k| - h[k] = @method_reference_factory.new(@doubled_module, k) + h[k] = @method_reference_class.new(@doubled_module, k) end end From 55115362bdb82e92c905711d80fa54de98ffd624 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Mon, 14 Oct 2013 11:11:52 -0700 Subject: [PATCH 0257/1179] Refactors logic a bit by introducing a `when_not_implemented` method --- lib/rspec/mocks/method_reference.rb | 5 +++++ lib/rspec/mocks/verifying_proxy.rb | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index d14d2ff88..d8dbe9ec3 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -32,6 +32,11 @@ def when_defined end end + # Yields to the block if the method is not implemented. + def when_not_implemented + yield unless implemented? + end + private def original_method @module_reference.when_loaded do |m| diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 200898e60..9b8a22416 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -57,7 +57,9 @@ def method_reference end def ensure_implemented(method_name) - if @doubled_module.defined? && !method_reference[method_name].implemented? + return unless @doubled_module.defined? + + method_reference[method_name].when_not_implemented do @error_generator.raise_unimplemented_error( @doubled_module, method_name From 48a63d11ccb71d349931d35bc9e32517af7710be Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Mon, 14 Oct 2013 11:27:59 -0700 Subject: [PATCH 0258/1179] Changelog for #422 [ci skip] --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 61d18e160..d8099ac3f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -38,6 +38,9 @@ Enhancements: * `allow(Klass.any_instance)` and `expect(Klass.any_instance)` now print a warning. This is usually a mistake, and users usually want `allow_any_instance_of` or `expect_any_instance_of` instead. (Sam Phippen) +* `instance_double` and `class_double` raise `ArgumentError` if the underlying + module is loaded and the arity of the method being invoked does not match the + arity of the method as it is actually implemented. (Andy Lindeman) Deprecations: From 3003936bfc9ed54fa2d6ed2da6ea96afec8ed65b Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 20 Oct 2013 18:27:40 -0400 Subject: [PATCH 0259/1179] Uses newer syntax --- spec/rspec/mocks/verifying_double_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 61947c642..bc93d67ac 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -134,7 +134,7 @@ def prevents(&block) it 'allows any method to be stubbed' do o = class_double('NonloadedClass') - o.stub(:undefined_instance_method).with(:arg).and_return(1) + allow(o).to receive(:undefined_instance_method).with(:arg).and_return(1) expect(o.undefined_instance_method(:arg)).to eq(1) end end From 2f8adc1581b5dc6efedccd9f5fdb499c7cefc1f7 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 20 Oct 2013 18:27:54 -0400 Subject: [PATCH 0260/1179] Uses the @proxy we already have loaded --- lib/rspec/mocks/method_double.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index d513062fa..7790f9897 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -71,7 +71,7 @@ def define_proxy_method # # @private def proxy_method_invoked(obj, *args, &block) - ::RSpec::Mocks.proxy_for(obj).message_received method_name, *args, &block + @proxy.message_received method_name, *args, &block end # @private From 17260f5a09da1c0f82ede82d964ab6b0011780f4 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 20 Oct 2013 18:28:46 -0400 Subject: [PATCH 0261/1179] Revises method name for clarity --- lib/rspec/mocks/method_reference.rb | 2 +- lib/rspec/mocks/verifying_proxy.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index d8dbe9ec3..4449719e9 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -33,7 +33,7 @@ def when_defined end # Yields to the block if the method is not implemented. - def when_not_implemented + def when_unimplemented yield unless implemented? end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 9b8a22416..e586aac61 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -59,7 +59,7 @@ def method_reference def ensure_implemented(method_name) return unless @doubled_module.defined? - method_reference[method_name].when_not_implemented do + method_reference[method_name].when_unimplemented do @error_generator.raise_unimplemented_error( @doubled_module, method_name From 8f3bcf9397bdf14ba932ec2de24d2d84f6b276b4 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Sun, 20 Oct 2013 21:16:05 -0400 Subject: [PATCH 0262/1179] Works around jruby/jruby#839 --- spec/rspec/mocks/verifying_message_expecation_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index ba6841f67..191b742e3 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -21,7 +21,7 @@ module Mocks describe 'when arity match fails' do it 'raises error' do - subject.method_reference = InstanceMethodReference.new(string_module_reference, :include?) + subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_arity_error). with(instance_of(ArityCalculator), 2) @@ -31,7 +31,7 @@ module Mocks describe 'when called with arguments' do it 'matches arity against the number of arguments' do - subject.method_reference = InstanceMethodReference.new(string_module_reference, :include?) + subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).not_to receive(:raise_arity_error) subject.with("abc123") @@ -40,14 +40,14 @@ module Mocks describe 'when called with any arguments matcher' do it 'does not try to match arity' do - subject.method_reference = InstanceMethodReference.new(string_module_reference, :include?) + subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) subject.with(any_args) end end describe 'when called with no arguments matcher' do it 'matches arity to 0' do - subject.method_reference = InstanceMethodReference.new(string_module_reference, :include?) + subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_arity_error). with(instance_of(ArityCalculator), 0) From f23ea9143cc9676acf98238e8f1b6eb065f82e2f Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 21 Oct 2013 17:14:47 +1100 Subject: [PATCH 0263/1179] Comment for version lock [skip ci] --- rspec-mocks.gemspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index af28e4d0e..2ef0830ef 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -24,8 +24,10 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.8.7' if RSpec::Mocks::Version::STRING =~ /[a-zA-Z]+/ + # pin to exact version for rc's and betas s.add_runtime_dependency "rspec-support", "= #{RSpec::Mocks::Version::STRING}" else + # pin to major/minor ignoring patch s.add_runtime_dependency "rspec-support", "~> #{RSpec::Mocks::Version::STRING.split('.')[0..1].concat(['0']).join('.')}" end From 88f2a7688e2000c12060bb6d4e01187d395dde10 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 21 Oct 2013 22:18:53 +1100 Subject: [PATCH 0264/1179] remove warnings --- spec/rspec/mocks/combining_implementation_instructions_spec.rb | 2 +- spec/rspec/mocks/matchers/receive_messages_spec.rb | 2 +- spec/rspec/mocks/space_spec.rb | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index aa06939c1..8a9de33a5 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -171,7 +171,7 @@ def verify_combined_implementation end it 'warns when the inner implementation block is overriden' do - expect(RSpec).to receive(:warning).with /overriding a previous implementation/ + expect(RSpec).to receive(:warning).with(/overriding a previous implementation/) double.stub(:foo).with(:arg) { :with_block }.at_least(:once) { :at_least_block } end diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 63e1e23ab..b28ad694f 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -85,7 +85,7 @@ module Mocks it 'fails with the correct location' do expect(obj).to receive_messages(:a => 1, :b => 2); line = __LINE__ - expect(expectation_error.backtrace[0]).to match /#{__FILE__}:#{line}/ + expect(expectation_error.backtrace[0]).to match(/#{__FILE__}:#{line}/) end it_behaves_like "complains when given blocks" diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index 774416217..3058fa78d 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -20,7 +20,8 @@ module RSpec::Mocks parent = parent_class.new child = child_class.new - grandparent_proxy = space.proxy_for(grandparent) + space.proxy_for(grandparent) + parent_proxy = space.proxy_for(parent) child_proxy = space.proxy_for(child) From fdbc72250326c3d61c1f7b504e2680a470922163 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 22 Oct 2013 21:10:03 +1100 Subject: [PATCH 0265/1179] Work around JRuby returning empty parameters on repeated calls. This fixes a JRuby bug where by repeated calls to parameters on unbound instance methods falsely return [] e.g. on JRuby 1.7.4 ```Ruby String.instance_method(:replace).parameters # => [[:req]] String.instance_method(:replace).parameters # => [] ``` And with this "fix" ```Ruby String.dup.instance_method(:replace).parameters # => [[:req]] String.dup.instance_method(:replace).parameters # => [[:req]] ``` --- lib/rspec/mocks/method_reference.rb | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 4449719e9..9a1e3040a 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -59,8 +59,20 @@ def method_implemented?(m) # definition of "implemented". However, it's the best we can do. alias method_defined? method_implemented? - def find_method(m) - m.instance_method(@method_name) + # works around the fact that repeated calls for method parameters will + # falsely return empty arrays on JRuby in certain circumstances, this + # is necessary here because we can't dup/clone UnboundMethods. + # + # This is necessary due to a bug in JRuby prior to 1.7.5 fixed in: + # https://github.com/jruby/jruby/commit/99a0613fe29935150d76a9a1ee4cf2b4f63f4a27 + if RUBY_PLATFORM == 'java' && JRUBY_VERSION.split('.')[-1].to_i < 5 + def find_method(m) + m.dup.instance_method(@method_name) + end + else + def find_method(m) + m.instance_method(@method_name) + end end end From 7378b390f64f5c06754229b60c706abc7505c3c5 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Tue, 1 Oct 2013 18:01:51 -0700 Subject: [PATCH 0266/1179] Provide object_double to create verifying doubles from object instances. --- Changelog.md | 2 + features/.nav | 1 + .../verifying_doubles/dynamic_classes.feature | 5 +- .../verifying_doubles/object_doubles.feature | 65 +++++++++++++++++ lib/rspec/mocks/error_generator.rb | 2 +- lib/rspec/mocks/example_methods.rb | 32 +++++++-- lib/rspec/mocks/method_reference.rb | 2 +- ...odule_reference.rb => object_reference.rb} | 32 ++++++++- lib/rspec/mocks/verifying_double.rb | 26 ++++--- spec/rspec/mocks/verifying_double_spec.rb | 72 +++++++++++++++++++ 10 files changed, 222 insertions(+), 17 deletions(-) create mode 100644 features/verifying_doubles/object_doubles.feature rename lib/rspec/mocks/{module_reference.rb => object_reference.rb} (51%) diff --git a/Changelog.md b/Changelog.md index d8099ac3f..dfd02d16b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -29,6 +29,8 @@ Enhancements: ported from `rspec-fire` (Xavier Shay). * `as_null_object` on a verifying double only responds to defined methods (Xavier Shay). +* Provide `object_double` to create verified doubles of specific object + instances (Xavier Shay). * Improved performance of double creation, particularly those with many attributes. (Xavier Shay) * Default value of `transfer_nested_constants` option for constant stubbing can diff --git a/features/.nav b/features/.nav index b90ebd9ef..40c12a3fb 100644 --- a/features/.nav +++ b/features/.nav @@ -25,6 +25,7 @@ - verifying_doubles: - introduction.feature - class_doubles.feature + - object_doubles.feature - dynamic_classes.feature - outside_rspec: - configuration.feature diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index 79a49f342..8d297fc88 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -3,11 +3,14 @@ Feature: Dynamic classes Verifying instance doubles do not support methods which the class reports to not exist since an actual instance of the class would be required to verify against. This is commonly the case when `method_missing` is used. - `ActiveRecord` does this to define methods from database columns. + `ActiveRecord` does this to define methods from database columns. If the + object has already been loaded you may consider using an `object_double`, but + that cannot work if you are testing in isolation. These types of methods are supported at class level, since `respond_to?` can be queried directly on the class. + Background: Given a file named "lib/fake_active_record.rb" with: """ruby diff --git a/features/verifying_doubles/object_doubles.feature b/features/verifying_doubles/object_doubles.feature new file mode 100644 index 000000000..62ce7f1e8 --- /dev/null +++ b/features/verifying_doubles/object_doubles.feature @@ -0,0 +1,65 @@ +Feature: Using an object double + + `object_double` can be used to create a double from an existing "template" + object, from which it verifies that any stubbed methods on the double also + exist on the template. This is useful for objects that are readily + constructable, but may have far-reaching side-effects such as talking to a + database or external API. In this case, using a double rather than the real + thing allows you to focus on the communication patterns of the object's + interface without having to worry about accidentally causing side-effects. + Object doubles can also be used to verify methods defined on an object using + `method_missing`, which is not possible with `instance_double`. + + In addition, `object_double` can be used with specific constant values, as + shown below. This is for niche situations, such as when dealing with + singleton objects. + + Scenario: doubling an existing object + Given a file named "spec/user_spec.rb" with: + """ruby + class User + # Don't want to accidentally trigger this! + def save; sleep 100; end + end + + def save_user(user) + "saved!" if user.save + end + + describe '#save_user' do + it 'renders message on success' do + user = object_double(User.new, :save => true) + expect(save_user(user)).to eq("saved!") + end + end + """ + When I run `rspec spec/user_spec.rb` + Then the examples should all pass + + + Scenario: doubling a constant object + Given a file named "spec/email_spec.rb" with: + """ruby + require 'logger' + + module MyApp + LOGGER = Logger.new("myapp") + end + + class Email + def self.send_to(recipient) + MyApp::LOGGER.info("Sent to #{recipient}") + # other emailing logic + end + end + + describe Email do + it 'logs a message when sending' do + logger = object_double("MyApp::LOGGER", :info => nil).as_stubbed_const + Email.send_to('hello@foo.com') + expect(logger).to have_received(:info).with("Sent to hello@foo.com") + end + end + """ + When I run `rspec spec/email_spec.rb` + Then the examples should all pass diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 6722895c3..2a21e9d2e 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -50,7 +50,7 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc # @private def raise_unimplemented_error(doubled_module, method_name) __raise "%s does not implement:\n %s" % [ - doubled_module.name, + doubled_module.description, method_name ] end diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 640b486af..67f540b27 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/module_reference' +require 'rspec/mocks/object_reference' module RSpec module Mocks @@ -42,7 +42,7 @@ def double(*args) # allowed to be stubbed. In all other ways it behaves like a # [double](double). def instance_double(doubled_class, *args) - declare_verifying_double(InstanceVerifyingDouble, doubled_class, *args) + declare_instance_or_class_double(InstanceVerifyingDouble, doubled_class, *args) end # @overload class_double(doubled_class) @@ -56,7 +56,27 @@ def instance_double(doubled_class, *args) # allowed to be stubbed. In all other ways it behaves like a # [double](double). def class_double(doubled_class, *args) - declare_verifying_double(ClassVerifyingDouble, doubled_class, *args) + declare_instance_or_class_double(ClassVerifyingDouble, doubled_class, *args) + end + + # @overload object_double(object_or_name) + # @overload object_double(object_or_name, stubs) + # @param object_or_name [String, Object] + # @param stubs [Hash] (optional) hash of message/return-value pairs + # @return ObjectVerifyingDouble + # + # Constructs a test double against a specific object. Only instance + # methods on the object are allowed to be stubbed. If a String argument + # is provided, it is assumed to reference a constant object which is used + # for verification. In all other ways it behaves like a [double](double). + def object_double(object_or_name, *args) + ref = if object_or_name.is_a?(String) + ModuleReference.new(object_or_name) + else + ObjectReference.new(object_or_name) + end + + declare_verifying_double(ObjectVerifyingDouble, ref, *args) end # Disables warning messages about expectations being set on nil. @@ -157,9 +177,13 @@ def self.included(klass) private - def declare_verifying_double(type, constant_or_name, *args) + def declare_instance_or_class_double(type, constant_or_name, *args) ref = ModuleReference.new(constant_or_name) + declare_verifying_double(type, ref, *args) + end + + def declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 9a1e3040a..ce8c2b429 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -77,7 +77,7 @@ def find_method(m) end # @private - class ClassMethodReference < MethodReference + class ObjectMethodReference < MethodReference private def method_implemented?(m) m.respond_to?(@method_name) diff --git a/lib/rspec/mocks/module_reference.rb b/lib/rspec/mocks/object_reference.rb similarity index 51% rename from lib/rspec/mocks/module_reference.rb rename to lib/rspec/mocks/object_reference.rb index 6b8faa970..692980f11 100644 --- a/lib/rspec/mocks/module_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -1,6 +1,32 @@ module RSpec module Mocks + # An abstraction in front of objects so that non-loaded objects can be + # worked with. The null case is for concrete objects that are always + # loaded. See `ModuleReference` for an example of non-loaded objects. + class ObjectReference + def initialize(object) + @object = object + end + + def description + @object.inspect + end + + def const_to_replace + raise ArgumentError, + "Can not perform constant replacement with an object." + end + + def defined? + true + end + + def when_loaded + yield @object + end + end + # Provides a consistent interface for dealing with modules that may or may # not be defined. # @@ -19,10 +45,12 @@ def defined? !!original_module end - def name - @name ||= @module.name + def const_to_replace + @name || @module.name end + alias_method :description, :const_to_replace + def when_loaded(&block) yield original_module if original_module end diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 19a23f296..e10c815cb 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -35,12 +35,9 @@ def __build_mock_proxy end end - # Similar to an InstanceVerifyingDouble, except that it verifies against - # public methods of the given class (i.e. the "class methods"). - # - # Module needs to be in the inheritance chain for transferring nested - # constants to work. - class ClassVerifyingDouble < Module + # An awkward module necessary because we cannot otherwise have + # ClassVerifyingDouble inherit from Module and still share these methods. + module ObjectVerifyingDoubleMethods include TestDouble include VerifyingDouble @@ -53,15 +50,28 @@ def initialize(doubled_module, *args) def __build_mock_proxy VerifyingProxy.new(self, @doubled_module, - ClassMethodReference + ObjectMethodReference ) end def as_stubbed_const(options = {}) - ConstantMutator.stub(@doubled_module.name, self, options) + ConstantMutator.stub(@doubled_module.const_to_replace, self, options) self end end + # Similar to an InstanceVerifyingDouble, except that it verifies against + # public methods of the given object. + class ObjectVerifyingDouble + include ObjectVerifyingDoubleMethods + end + + # Effectively the same as an ObjectVerifyingDouble (since a class is a type + # of object), except with Module in the inheritance chain so that + # transferring nested constants to work. + class ClassVerifyingDouble < Module + include ObjectVerifyingDoubleMethods + end + end end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index bc93d67ac..0be381c57 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -3,6 +3,7 @@ class LoadedClass M = :m N = :n + INSTANCE = LoadedClass.new def defined_instance_method; end def self.defined_class_method; end @@ -122,6 +123,18 @@ def prevents(&block) prevents { o.undefined_method } end end + + it 'cannot be constructed with a non-module object' do + expect { + instance_double(Object.new) + }.to raise_error(/Module or String expected/) + end + + it 'can be constructed with a struct' do + o = instance_double(Struct.new(:defined_method), :defined_method => 1) + + expect(o.defined_method).to eq(1) + end end describe 'class doubles' do @@ -229,8 +242,67 @@ def prevents(&block) prevents { o.undefined_method } end end + + it 'cannot be constructed with a non-module object' do + expect { + class_double(Object.new) + }.to raise_error(/Module or String expected/) + end end + describe 'object doubles' do + it 'replaces an unloaded constant' do + o = object_double("LoadedClass::NOINSTANCE").as_stubbed_const + + expect(LoadedClass::NOINSTANCE).to eq(o) + + expect(o).to receive(:undefined_instance_method) + o.undefined_instance_method + end + + it 'replaces a constant by name and verifies instances methods' do + o = object_double("LoadedClass::INSTANCE").as_stubbed_const + + expect(LoadedClass::INSTANCE).to eq(o) + + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_class_method) } + prevents { o.defined_instance_method } + + expect(o).to receive(:defined_instance_method) + o.defined_instance_method + end + + it 'can create a double that matches the interface of any arbitrary object' do + o = object_double(LoadedClass.new) + + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_class_method) } + prevents { o.defined_instance_method } + + expect(o).to receive(:defined_instance_method) + o.defined_instance_method + end + + it 'does not allow transferring constants to an object' do + expect { + object_double("LoadedClass::INSTANCE"). + as_stubbed_const(:transfer_nested_constants => true) + }.to raise_error(/Cannot transfer nested constants/) + end + + it 'does not allow as_stubbed_constant for real objects' do + expect { + object_double(LoadedClass.new).as_stubbed_const + }.to raise_error(/Can not perform constant replacement with an object/) + end + + it 'is not a module' do + expect(object_double("LoadedClass::INSTANCE")).to_not be_a(Module) + end + end + + describe 'when verify_doubled_constant_names config option is set' do include_context "with isolated configuration" From e9daa566397c35237805c001bf5deaccab12007a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 22 Oct 2013 23:38:41 -0700 Subject: [PATCH 0267/1179] Refactor: reify the 3 types of object references we have. --- lib/rspec/mocks/example_methods.rb | 19 ++--- lib/rspec/mocks/object_reference.rb | 71 +++++++++++++------ .../verifying_message_expecation_spec.rb | 2 +- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 67f540b27..0f5bf5d2f 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -42,7 +42,8 @@ def double(*args) # allowed to be stubbed. In all other ways it behaves like a # [double](double). def instance_double(doubled_class, *args) - declare_instance_or_class_double(InstanceVerifyingDouble, doubled_class, *args) + ref = ObjectReference.for(doubled_class) + declare_verifying_double(InstanceVerifyingDouble, ref, *args) end # @overload class_double(doubled_class) @@ -56,7 +57,8 @@ def instance_double(doubled_class, *args) # allowed to be stubbed. In all other ways it behaves like a # [double](double). def class_double(doubled_class, *args) - declare_instance_or_class_double(ClassVerifyingDouble, doubled_class, *args) + ref = ObjectReference.for(doubled_class) + declare_verifying_double(ClassVerifyingDouble, ref, *args) end # @overload object_double(object_or_name) @@ -70,12 +72,7 @@ def class_double(doubled_class, *args) # is provided, it is assumed to reference a constant object which is used # for verification. In all other ways it behaves like a [double](double). def object_double(object_or_name, *args) - ref = if object_or_name.is_a?(String) - ModuleReference.new(object_or_name) - else - ObjectReference.new(object_or_name) - end - + ref = ObjectReference.for(object_or_name, :allow_direct_object_refs) declare_verifying_double(ObjectVerifyingDouble, ref, *args) end @@ -177,12 +174,6 @@ def self.included(klass) private - def declare_instance_or_class_double(type, constant_or_name, *args) - ref = ModuleReference.new(constant_or_name) - - declare_verifying_double(type, ref, *args) - end - def declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 692980f11..f1162f8db 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -1,10 +1,29 @@ module RSpec module Mocks - # An abstraction in front of objects so that non-loaded objects can be - # worked with. The null case is for concrete objects that are always - # loaded. See `ModuleReference` for an example of non-loaded objects. + # @api private class ObjectReference + # Returns an appropriate Object or Module reference based + # on the given argument. + def self.for(object_module_or_name, allow_direct_object_refs = false) + case object_module_or_name + when Module then DirectModuleReference.new(object_module_or_name) + when String then NamedObjectReference.new(object_module_or_name) + else + if allow_direct_object_refs + DirectObjectReference.new(object_module_or_name) + else + raise ArgumentError, + "Module or String expected, got #{object_module_or_name.inspect}" + end + end + end + end + + # Used when an object is passed to `object_double`. + # Represents a reference to that object. + # @api private + class DirectObjectReference def initialize(object) @object = object end @@ -27,39 +46,45 @@ def when_loaded end end - # Provides a consistent interface for dealing with modules that may or may - # not be defined. - # - # @private - class ModuleReference - def initialize(module_or_name) - case module_or_name - when Module then @module = module_or_name - when String then @name = module_or_name - else raise ArgumentError, - "Module or String expected, got #{module_or_name.inspect}" - end + # Used when a module is passed to `class_double` or `instance_double`. + # Represents a reference to that module. + # @api private + class DirectModuleReference < DirectObjectReference + def const_to_replace + @object.name + end + alias description const_to_replace + end + + # Used when a string is passed to `class_double`, `instance_double` + # or `object_double`. + # Represents a reference to the object named (via a constant lookup) + # by the string. + # @api private + class NamedObjectReference + def initialize(const_name) + @const_name = const_name end def defined? - !!original_module + !!object end def const_to_replace - @name || @module.name + @const_name end - - alias_method :description, :const_to_replace + alias description const_to_replace def when_loaded(&block) - yield original_module if original_module + yield object if object end - private + private - def original_module - @module ||= Constant.original(@name).original_value + def object + @object ||= Constant.original(@const_name).original_value end end end end + diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 191b742e3..7321b1f32 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -5,7 +5,7 @@ module Mocks describe VerifyingMessageExpectation do describe '#with' do let(:error_generator) { double.as_null_object } - let(:string_module_reference) { ModuleReference.new(String) } + let(:string_module_reference) { DirectModuleReference.new(String) } subject { null = double.as_null_object From 477cd1f33eef6d853b4560e4a6751cef5c511215 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 22 Oct 2013 23:40:59 -0700 Subject: [PATCH 0268/1179] Clarify docs. It's confusing to talk about "instance methods" for `object_double` because it deals with `object.methods`, not `object.instance_methods`. --- lib/rspec/mocks/example_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 0f5bf5d2f..39faf2394 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -67,8 +67,8 @@ def class_double(doubled_class, *args) # @param stubs [Hash] (optional) hash of message/return-value pairs # @return ObjectVerifyingDouble # - # Constructs a test double against a specific object. Only instance - # methods on the object are allowed to be stubbed. If a String argument + # Constructs a test double against a specific object. Only the methods + # the object responds to are allowed to be stubbed. If a String argument # is provided, it is assumed to reference a constant object which is used # for verification. In all other ways it behaves like a [double](double). def object_double(object_or_name, *args) From b727d679824af4a05acdac466ad4829c2512a4a2 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 23 Oct 2013 10:07:21 +1100 Subject: [PATCH 0269/1179] refactor order group to work on all expectations --- lib/rspec/mocks/message_expectation.rb | 8 ++++++- lib/rspec/mocks/order_group.rb | 29 +++++++++++++++++++------- spec/rspec/mocks/order_group_spec.rb | 27 ++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 spec/rspec/mocks/order_group_spec.rb diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 447f3609b..b22ea6d6b 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -55,6 +55,8 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @expected_received_count = expected_received_count @argument_list_matcher = ArgumentListMatcher::MATCH_ALL @order_group = expectation_ordering + @order_group.register(self) + @ordered = false @at_least = @at_most = @exactly = nil @args_to_yield = [] @failed_fast = nil @@ -462,11 +464,15 @@ def twice(&block) # api.should_receive(:finish).ordered def ordered(&block) self.inner_implementation_action = block - @order_group.register(self) @ordered = true self end + # @private + def ordered? + @ordered + end + # @private def negative_expectation_for?(message) @message == message && negative? diff --git a/lib/rspec/mocks/order_group.rb b/lib/rspec/mocks/order_group.rb index b86cccab3..e9be1ec16 100644 --- a/lib/rspec/mocks/order_group.rb +++ b/lib/rspec/mocks/order_group.rb @@ -3,38 +3,53 @@ module Mocks # @private class OrderGroup def initialize - @ordering = Array.new + @expectations = [] + @index = 0 end # @private def register(expectation) - @ordering << expectation + @expectations << expectation end # @private def ready_for?(expectation) - @ordering.first == expectation + remaining_expectations.find(&:ordered?) == expectation end # @private def consume - @ordering.shift + remaining_expectations.each_with_index do |expectation, index| + if expectation.ordered? + @index += index + 1 + return expectation + end + end + nil end # @private def handle_order_constraint(expectation) - return unless @ordering.include?(expectation) + return unless expectation.ordered? && @expectations.include?(expectation) return consume if ready_for?(expectation) expectation.raise_out_of_order_error end def clear - @ordering.clear + @index = 0 + @expectations.clear end def empty? - @ordering.empty? + @expectations.empty? end + + private + + def remaining_expectations + @expectations[@index..-1] || [] + end + end end end diff --git a/spec/rspec/mocks/order_group_spec.rb b/spec/rspec/mocks/order_group_spec.rb new file mode 100644 index 000000000..3c4de181c --- /dev/null +++ b/spec/rspec/mocks/order_group_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' +describe 'OrderGroup' do + let(:order_group) { ::RSpec::Mocks::OrderGroup.new } + + describe '#consume' do + let(:ordered_1) { double :ordered? => true } + let(:ordered_2) { double :ordered? => true } + let(:unordered) { double :ordered? => false } + + before do + order_group.register unordered + order_group.register ordered_1 + order_group.register unordered + order_group.register ordered_2 + order_group.register unordered + order_group.register unordered + end + + it 'returns the first ordered? expectation' do + expect(order_group.consume).to eq ordered_1 + end + it 'keeps returning ordered? expectation until all are returned' do + expectations = 3.times.map { order_group.consume } + expect(expectations).to eq [ordered_1, ordered_2, nil] + end + end +end From 59a95de5d3d392dbca790f481f767cd106013997 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 23 Oct 2013 10:07:46 +1100 Subject: [PATCH 0270/1179] expose ordered on have_received --- lib/rspec/mocks/matchers/have_received.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index ff0ff7d6e..fcac77aa5 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -4,7 +4,7 @@ module Matchers class HaveReceived COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice) ARGS_CONSTRAINTS = %w(with) - CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w(ordered) def initialize(method_name, &block) @method_name = method_name @@ -50,7 +50,7 @@ def description end end - private + private def expect expectation = mock_proxy.build_expectation(@method_name) From 9dcac4183be977ec13be984f49c0240b2fa16fb1 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 23 Oct 2013 13:57:01 +1100 Subject: [PATCH 0271/1179] recording invocation ordering --- lib/rspec/mocks/order_group.rb | 6 ++++++ lib/rspec/mocks/proxy.rb | 11 +++++++++++ lib/rspec/mocks/space.rb | 4 ++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/order_group.rb b/lib/rspec/mocks/order_group.rb index e9be1ec16..d3ab2396a 100644 --- a/lib/rspec/mocks/order_group.rb +++ b/lib/rspec/mocks/order_group.rb @@ -4,6 +4,7 @@ module Mocks class OrderGroup def initialize @expectations = [] + @invocation_order = [] @index = 0 end @@ -12,6 +13,10 @@ def register(expectation) @expectations << expectation end + def invoked(object, message) + @invocation_order << [object, message] + end + # @private def ready_for?(expectation) remaining_expectations.find(&:ordered?) == expectation @@ -37,6 +42,7 @@ def handle_order_constraint(expectation) def clear @index = 0 + @invocation_order.clear @expectations.clear end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 2237d3deb..f5b24ba12 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -2,6 +2,10 @@ module RSpec module Mocks # @private class Proxy + class NullOrderGroup + def invoked(object, message) + end + end # @private def initialize(object, name=nil, options={}) @@ -11,9 +15,15 @@ def initialize(object, name=nil, options={}) @expectation_ordering = RSpec::Mocks::space.expectation_ordering @messages_received = [] @options = options + @order_group = NullOrderGroup.new @null_object = false end + def with_order_group(group) + @order_group = group + self + end + # @private attr_reader :object @@ -139,6 +149,7 @@ def has_negative_expectation?(message) # @private def record_message_received(message, *args, &block) + @order_group.invoked(object, message) @messages_received << [message, args, block] end diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 6e8ec55b2..44b35ca09 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -55,9 +55,9 @@ def proxy_for(object) proxies.fetch(id) do proxies[id] = case object when NilClass then ProxyForNil.new - when TestDouble then object.__build_mock_proxy + when TestDouble then object.__build_mock_proxy.with_order_group(expectation_ordering) else - PartialMockProxy.new(object) + PartialMockProxy.new(object).with_order_group(expectation_ordering) end end end From 056d2fccf8d1ca3b987440b0336fa5fc7dfc919e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 23 Oct 2013 13:56:41 +1100 Subject: [PATCH 0272/1179] verifying ordering of spies --- Changelog.md | 1 + lib/rspec/mocks/matchers/have_received.rb | 8 +++---- lib/rspec/mocks/message_expectation.rb | 4 ++++ lib/rspec/mocks/order_group.rb | 21 ++++++++++++++++++ .../mocks/matchers/have_received_spec.rb | 22 +++++++++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index dfd02d16b..4a5539bb0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -43,6 +43,7 @@ Enhancements: * `instance_double` and `class_double` raise `ArgumentError` if the underlying module is loaded and the arity of the method being invoked does not match the arity of the method as it is actually implemented. (Andy Lindeman) +* Spies can now check their invocation ordering is correct. (Jon Rowe) Deprecations: diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index fcac77aa5..c87faa1ec 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -21,14 +21,14 @@ def matches?(subject, &block) @block ||= block @subject = subject @expectation = expect - expected_messages_received? + expected_messages_received_in_order? end def does_not_match?(subject) @subject = subject ensure_count_unconstrained @expectation = expect.never - expected_messages_received? + expected_messages_received_in_order? end def failure_message @@ -84,9 +84,9 @@ def generate_failure_message error.message end - def expected_messages_received? + def expected_messages_received_in_order? mock_proxy.replay_received_message_on @expectation, &@block - @expectation.expected_messages_received? + @expectation.expected_messages_received? && @expectation.expected_ordering_received? end def mock_proxy diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index b22ea6d6b..1e2f9b8d1 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -279,6 +279,10 @@ def expected_messages_received? ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count? end + def expected_ordering_received? + !@ordered || @order_group.verify_invocation_order(self) + end + # @private def ignoring_args? @expected_received_count == :any diff --git a/lib/rspec/mocks/order_group.rb b/lib/rspec/mocks/order_group.rb index d3ab2396a..da706c6a5 100644 --- a/lib/rspec/mocks/order_group.rb +++ b/lib/rspec/mocks/order_group.rb @@ -40,6 +40,11 @@ def handle_order_constraint(expectation) expectation.raise_out_of_order_error end + def verify_invocation_order(expectation) + expectation.raise_out_of_order_error unless expectations_invoked_in_order? + true + end + def clear @index = 0 @invocation_order.clear @@ -56,6 +61,22 @@ def remaining_expectations @expectations[@index..-1] || [] end + def expectations_invoked_in_order? + invoked_expectations == expected_invocations + end + + def invoked_expectations + @expectations.select { |e| e.ordered? && @invocation_order.include?([e.orig_object,e.message]) } + end + + def expected_invocations + @invocation_order.map { |invocation| expectation_for(*invocation) }.compact + end + + def expectation_for(object, message) + @expectations.find { |e| e.orig_object == object && e.message == message } + end + end end end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 65601b5ec..8fc3030a6 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -247,6 +247,28 @@ module Mocks end end end + + context 'ordered' do + let(:dbl) { double :one => 1, :two => 2 } + + it 'passes when the messages were received in order' do + dbl.one + dbl.two + + expect(dbl).to have_received(:one).ordered + expect(dbl).to have_received(:two).ordered + end + + it 'fails when the messages are received out of order' do + dbl.two + dbl.one + + expect { + expect(dbl).to have_received(:one).ordered + expect(dbl).to have_received(:two).ordered + }.to raise_error(/received :two out of order/m) + end + end end describe "expect(...).not_to have_received" do From 939236f0794a4be3c7ca9d78489a72d823e55df0 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 23 Oct 2013 15:52:49 +1100 Subject: [PATCH 0273/1179] dont use a fluent style interface to inject order group --- lib/rspec/mocks/proxy.rb | 13 ++----------- lib/rspec/mocks/proxy_for_nil.rb | 4 ++-- lib/rspec/mocks/space.rb | 6 +++--- lib/rspec/mocks/test_double.rb | 4 ++-- lib/rspec/mocks/verifying_double.rb | 8 ++++---- lib/rspec/mocks/verifying_proxy.rb | 4 ++-- 6 files changed, 15 insertions(+), 24 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index f5b24ba12..c75db8b05 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -2,28 +2,19 @@ module RSpec module Mocks # @private class Proxy - class NullOrderGroup - def invoked(object, message) - end - end # @private - def initialize(object, name=nil, options={}) + def initialize(object, order_group, name=nil, options={}) @object = object + @order_group = order_group @name = name @error_generator = ErrorGenerator.new(object, name) @expectation_ordering = RSpec::Mocks::space.expectation_ordering @messages_received = [] @options = options - @order_group = NullOrderGroup.new @null_object = false end - def with_order_group(group) - @order_group = group - self - end - # @private attr_reader :object diff --git a/lib/rspec/mocks/proxy_for_nil.rb b/lib/rspec/mocks/proxy_for_nil.rb index 075033e90..b58cb3de7 100644 --- a/lib/rspec/mocks/proxy_for_nil.rb +++ b/lib/rspec/mocks/proxy_for_nil.rb @@ -3,9 +3,9 @@ module Mocks # @private class ProxyForNil < Proxy - def initialize + def initialize(order_group) @warn_about_expectations = true - super nil + super nil, order_group end attr_accessor :warn_about_expectations alias warn_about_expectations? warn_about_expectations diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 44b35ca09..9d94cd292 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -54,10 +54,10 @@ def proxy_for(object) id = id_for(object) proxies.fetch(id) do proxies[id] = case object - when NilClass then ProxyForNil.new - when TestDouble then object.__build_mock_proxy.with_order_group(expectation_ordering) + when NilClass then ProxyForNil.new(expectation_ordering) + when TestDouble then object.__build_mock_proxy(expectation_ordering) else - PartialMockProxy.new(object).with_order_group(expectation_ordering) + PartialMockProxy.new(object, expectation_ordering) end end end diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 17173350d..1cc9b58f7 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -61,8 +61,8 @@ def respond_to?(message, incl_private=false) end # @private - def __build_mock_proxy - Proxy.new(self, @name) + def __build_mock_proxy(order_group) + Proxy.new(self, order_group, @name) end private diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index e10c815cb..785be3ff3 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -27,8 +27,8 @@ def initialize(doubled_module, *args) __initialize_as_test_double(doubled_module, *args) end - def __build_mock_proxy - VerifyingProxy.new(self, + def __build_mock_proxy(order_group) + VerifyingProxy.new(self, order_group, @doubled_module, InstanceMethodReference ) @@ -47,8 +47,8 @@ def initialize(doubled_module, *args) __initialize_as_test_double(doubled_module, *args) end - def __build_mock_proxy - VerifyingProxy.new(self, + def __build_mock_proxy(order_group) + VerifyingProxy.new(self, order_group, @doubled_module, ObjectMethodReference ) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index e586aac61..3907a28cf 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -19,8 +19,8 @@ module Mocks # # @api private class VerifyingProxy < Proxy - def initialize(object, name, method_reference_class) - super(object) + def initialize(object, order_group, name, method_reference_class) + super(object, order_group) @object = object @doubled_module = name @method_reference_class = method_reference_class From 0ada1b98779126563c2bfb9dc407fd4b78a635c0 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 24 Oct 2013 09:18:45 +1100 Subject: [PATCH 0274/1179] refactor to use specific message value object --- lib/rspec/mocks/order_group.rb | 12 ++++++------ lib/rspec/mocks/proxy.rb | 7 ++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/order_group.rb b/lib/rspec/mocks/order_group.rb index da706c6a5..8b6f07df8 100644 --- a/lib/rspec/mocks/order_group.rb +++ b/lib/rspec/mocks/order_group.rb @@ -13,8 +13,8 @@ def register(expectation) @expectations << expectation end - def invoked(object, message) - @invocation_order << [object, message] + def invoked(message) + @invocation_order << message end # @private @@ -66,15 +66,15 @@ def expectations_invoked_in_order? end def invoked_expectations - @expectations.select { |e| e.ordered? && @invocation_order.include?([e.orig_object,e.message]) } + @expectations.select { |e| e.ordered? && @invocation_order.include?(e) } end def expected_invocations - @invocation_order.map { |invocation| expectation_for(*invocation) }.compact + @invocation_order.map { |invocation| expectation_for(invocation) }.compact end - def expectation_for(object, message) - @expectations.find { |e| e.orig_object == object && e.message == message } + def expectation_for(message) + @expectations.find { |e| message == e } end end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index c75db8b05..e1c5f4df7 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -2,6 +2,11 @@ module RSpec module Mocks # @private class Proxy + SpecificMessage = Struct.new(:object,:message) do + def ==(expectation) + expectation.orig_object == object && expectation.message == message + end + end # @private def initialize(object, order_group, name=nil, options={}) @@ -140,7 +145,7 @@ def has_negative_expectation?(message) # @private def record_message_received(message, *args, &block) - @order_group.invoked(object, message) + @order_group.invoked SpecificMessage.new(object, message) @messages_received << [message, args, block] end From 9c5a718b4e2e608b1597f27af484efd68ba2cc94 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 24 Oct 2013 09:50:43 +1100 Subject: [PATCH 0275/1179] rename method to improve clarity --- lib/rspec/mocks/matchers/have_received.rb | 2 +- lib/rspec/mocks/message_expectation.rb | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index c87faa1ec..068ee2c9f 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -86,7 +86,7 @@ def generate_failure_message def expected_messages_received_in_order? mock_proxy.replay_received_message_on @expectation, &@block - @expectation.expected_messages_received? && @expectation.expected_ordering_received? + @expectation.expected_messages_received? && @expectation.ensure_expected_ordering_received! end def mock_proxy diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 1e2f9b8d1..ce4bfc586 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -279,8 +279,9 @@ def expected_messages_received? ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count? end - def expected_ordering_received? - !@ordered || @order_group.verify_invocation_order(self) + def ensure_expected_ordering_received! + @order_group.verify_invocation_order(self) if @ordered + true end # @private From 8e44309a681ab4223358316534d99294c79207c5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 24 Oct 2013 10:37:29 +1100 Subject: [PATCH 0276/1179] allow suppresion of local dirs --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index b1031d537..9f76e5f8a 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ gemspec %w[rspec rspec-core rspec-expectations rspec-support].each do |lib| library_path = File.expand_path("../../#{lib}", __FILE__) - if File.exist?(library_path) + if File.exist?(library_path) && !ENV['USE_GIT_REPOS'] gem lib, :path => library_path else gem lib, :git => "git://github.com/rspec/#{lib}.git" From 1c29ed53c79cf99311531f2c2243b381f4c2697a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 24 Oct 2013 14:43:25 +1100 Subject: [PATCH 0277/1179] fix regression with multiple calls to ordered expectations, fixes rspec/rspec-core --- lib/rspec/mocks/order_group.rb | 2 +- spec/rspec/mocks/mock_ordering_spec.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/order_group.rb b/lib/rspec/mocks/order_group.rb index 8b6f07df8..52f882ed9 100644 --- a/lib/rspec/mocks/order_group.rb +++ b/lib/rspec/mocks/order_group.rb @@ -35,7 +35,7 @@ def consume # @private def handle_order_constraint(expectation) - return unless expectation.ordered? && @expectations.include?(expectation) + return unless expectation.ordered? && remaining_expectations.include?(expectation) return consume if ready_for?(expectation) expectation.raise_out_of_order_error end diff --git a/spec/rspec/mocks/mock_ordering_spec.rb b/spec/rspec/mocks/mock_ordering_spec.rb index f87218922..3754d65e5 100644 --- a/spec/rspec/mocks/mock_ordering_spec.rb +++ b/spec/rspec/mocks/mock_ordering_spec.rb @@ -16,6 +16,17 @@ module Mocks @double.three end + it "passes when messages are received in order" do + @double.stub(:something) + @double.should_receive(:one).ordered + @double.should_receive(:two).ordered + @double.should_receive(:three).at_least(:once).ordered + @double.one + @double.two + @double.three + @double.three + end + it "passes when messages are received in order across objects" do a = double("a") b = double("b") From d3625299d00dae1af31a09f1db946fae875f2112 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 27 Oct 2013 16:54:01 +1100 Subject: [PATCH 0278/1179] use ruby 2.1 preview --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index db5172e3c..9feb1ed0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ rvm: - rbx-18mode - rbx-19mode - 2.0.0 + - 2.1.0-preview1 matrix: allow_failures: - rvm: rbx-18mode From 70a4dd7a4ac75bc582e68fd61566754a0c300c40 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 27 Oct 2013 18:17:05 -0700 Subject: [PATCH 0279/1179] Pin to mime-types 1.X in gemspec. --- Gemfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index 9f76e5f8a..ee4ee1ad7 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,9 @@ end ### dep for ci/coverage gem 'coveralls', :require => false +# mime-types 2 requires ruby 1.8, so we have to specify an old version. +gem 'mime-types', '~> 1.0' + platforms :jruby do gem "jruby-openssl" end From 7ae7c10fa6c989e9b50d2c1bac98e4a4b118a66c Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Thu, 24 Oct 2013 18:30:16 -0700 Subject: [PATCH 0280/1179] Add verify_partial_doubles configuration option. When set treats partial doubles as object doubles, verifying arity and method existence. --- Changelog.md | 2 + features/.nav | 1 + .../verifying_doubles/partial_doubles.feature | 34 +++++++ lib/rspec/mocks/any_instance/recorder.rb | 4 + lib/rspec/mocks/configuration.rb | 12 +++ lib/rspec/mocks/method_double.rb | 18 +++- lib/rspec/mocks/space.rb | 6 +- lib/rspec/mocks/verifying_proxy.rb | 94 ++++++++++++++----- spec/rspec/mocks/partial_mock_spec.rb | 71 ++++++++++++++ spec/rspec/mocks/verifying_double_spec.rb | 5 - spec/spec_helper.rb | 8 ++ 11 files changed, 222 insertions(+), 33 deletions(-) create mode 100644 features/verifying_doubles/partial_doubles.feature diff --git a/Changelog.md b/Changelog.md index 4a5539bb0..14da506aa 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,6 +31,8 @@ Enhancements: (Xavier Shay). * Provide `object_double` to create verified doubles of specific object instances (Xavier Shay). +* Provide 'verify_partial_doubles` configuration that provides `object_double` + like verification behaviour on partial mocks. (Xavier Shay) * Improved performance of double creation, particularly those with many attributes. (Xavier Shay) * Default value of `transfer_nested_constants` option for constant stubbing can diff --git a/features/.nav b/features/.nav index 40c12a3fb..1ca3a0688 100644 --- a/features/.nav +++ b/features/.nav @@ -27,6 +27,7 @@ - class_doubles.feature - object_doubles.feature - dynamic_classes.feature + - partial_doubles.feature - outside_rspec: - configuration.feature - standalone.feature diff --git a/features/verifying_doubles/partial_doubles.feature b/features/verifying_doubles/partial_doubles.feature new file mode 100644 index 000000000..c4387031f --- /dev/null +++ b/features/verifying_doubles/partial_doubles.feature @@ -0,0 +1,34 @@ +Feature: Partial doubles + + When the `verify_partial_doubles` configuration option is set, the same arity + and method existince checks that are performed for `object_double` are also + performed on partial doubles. You should set this unless you have a good + reason not to. It defaults to off only for backwards compatibility. + + Scenario: doubling an existing object + Given a file named "spec/user_spec.rb" with: + """ruby + class User + def save; false; end + end + + def save_user(user) + "saved!" if user.save + end + + RSpec.configure do |config| + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + end + + describe '#save_user' do + it 'renders message on success' do + user = User.new + expect(user).to receive(:saave).and_return(true) # Typo in name + expect(save_user(user)).to eq("saved!") + end + end + """ + When I run `rspec spec/user_spec.rb` + Then the output should contain "1 example, 1 failure" diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 896039ffe..9aa842f3b 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -172,6 +172,10 @@ def stop_observing!(method_name) end def observe!(method_name) + if RSpec::Mocks.configuration.verify_partial_doubles? + raise MockExpectationError unless @klass.method_defined?(method_name) + end + stop_observing!(method_name) if already_observing?(method_name) @observed_methods << method_name backup_method!(method_name) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 8a5e2c710..8a4d6901e 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -7,6 +7,7 @@ def initialize @yield_receiver_to_any_instance_implementation_blocks = true @verify_doubled_constant_names = false @transfer_nested_constants = false + @verify_partial_doubles = false end def yield_receiver_to_any_instance_implementation_blocks? @@ -82,6 +83,17 @@ def transfer_nested_constants=(val) @transfer_nested_constants = val end + # When set to true, partial mocks will be verified the same as object + # doubles. Any stubs will have their arity checked against the original + # method, and methods that do not exist cannot be stubbed. + def verify_partial_doubles=(val) + @verify_partial_doubles = !!val + end + + def verify_partial_doubles? + @verify_partial_doubles + end + # @api private # Resets the configured syntax to the default. def reset_syntaxes_to_default diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 7790f9897..6ec7b5c6a 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -19,11 +19,20 @@ def initialize(object, method_name, proxy) end def original_method - @original_method ||= Proc.new do |*args, &block| - @object.__send__(:method_missing, @method_name, *args, &block) - end + # If original method is not present, uses the `method_missing` + # handler of the object. This accounts for cases where the user has not + # correctly defined `respond_to?`, and also 1.8 which does not provide + # method handles for missing methods even if `respond_to?` is correct. + @original_method ||= + @method_stasher.original_method || + @proxy.method_handle_for(method_name) || + Proc.new do |*args, &block| + @object.__send__(:method_missing, @method_name, *args, &block) + end end + alias_method :save_original_method!, :original_method + # @private def visibility if TestDouble === @object @@ -53,8 +62,7 @@ def configure_method def define_proxy_method return if @method_is_proxied - @original_method = @method_stasher.original_method || - @proxy.method_handle_for(method_name) + save_original_method! object_singleton_class.class_exec(self, method_name, visibility) do |method_double, method_name, visibility| define_method(method_name) do |*args, &block| diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 9d94cd292..e7b8f04ae 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -57,7 +57,11 @@ def proxy_for(object) when NilClass then ProxyForNil.new(expectation_ordering) when TestDouble then object.__build_mock_proxy(expectation_ordering) else - PartialMockProxy.new(object, expectation_ordering) + if RSpec::Mocks.configuration.verify_partial_doubles? + VerifyingPartialMockProxy.new(object, expectation_ordering) + else + PartialMockProxy.new(object, expectation_ordering) + end end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 3907a28cf..a934fde21 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -4,6 +4,34 @@ module RSpec module Mocks + module VerifyingProxyMethods + def add_stub(location, method_name, opts={}, &implementation) + ensure_implemented(method_name) + super + end + + def add_simple_stub(method_name, *args) + ensure_implemented(method_name) + super + end + + def add_message_expectation(location, method_name, opts={}, &block) + ensure_implemented(method_name) + super + end + + def ensure_implemented(method_name) + return unless @doubled_module.defined? + + method_reference[method_name].when_unimplemented do + @error_generator.raise_unimplemented_error( + @doubled_module, + method_name + ) + end + end + end + # A verifying proxy mostly acts like a normal proxy, except that it # contains extra logic to try and determine the validity of any expectation # set on it. This includes whether or not methods have been defined and the @@ -19,6 +47,8 @@ module Mocks # # @api private class VerifyingProxy < Proxy + include VerifyingProxyMethods + def initialize(object, order_group, name, method_reference_class) super(object, order_group) @object = object @@ -26,21 +56,6 @@ def initialize(object, order_group, name, method_reference_class) @method_reference_class = method_reference_class end - def add_stub(location, method_name, opts={}, &implementation) - ensure_implemented(method_name) - super - end - - def add_simple_stub(method_name, *args) - ensure_implemented(method_name) - super - end - - def add_message_expectation(location, method_name, opts={}, &block) - ensure_implemented(method_name) - super - end - # A custom method double is required to pass through a way to lookup # methods to determine their arity. This is only relevant if the doubled # class is loaded. @@ -55,17 +70,25 @@ def method_reference h[k] = @method_reference_class.new(@doubled_module, k) end end + end - def ensure_implemented(method_name) - return unless @doubled_module.defined? + class VerifyingPartialMockProxy < PartialMockProxy + include VerifyingProxyMethods - method_reference[method_name].when_unimplemented do - @error_generator.raise_unimplemented_error( - @doubled_module, - method_name - ) + def initialize(object, expectation_ordering) + super(object, expectation_ordering) + @doubled_module = DirectObjectReference.new(object) + end + + # A custom method double is required to pass through a way to lookup + # methods to determine their arity. + def method_double + @method_double ||= Hash.new do |h,k| + h[k] = VerifyingExistingMethodDouble.new(object, k, self) end end + + alias_method :method_reference, :method_double end # @api private @@ -99,5 +122,32 @@ def ensure_arity!(arity) end end end + + # @api private + # + # A VerifyingMethodDouble fetches the method to verify against from the + # original object, using a MethodReference. This works for pure doubles, + # but when the original object is itself the one being modified we need to + # collapse the reference and the method double into a single object so that + # we can access the original pristine method definition. + class VerifyingExistingMethodDouble < VerifyingMethodDouble + def initialize(object, method_name, proxy) + super(object, method_name, proxy, self) + + @valid_method = object.respond_to?(method_name) + + # Trigger an eager find of the original method since if we find it any + # later we end up getting a stubbed method with incorrect arity. + save_original_method! + end + + def when_defined + yield original_method + end + + def when_unimplemented + yield unless @valid_method + end + end end end diff --git a/spec/rspec/mocks/partial_mock_spec.rb b/spec/rspec/mocks/partial_mock_spec.rb index 28a6f91c5..7fc0c8e7f 100644 --- a/spec/rspec/mocks/partial_mock_spec.rb +++ b/spec/rspec/mocks/partial_mock_spec.rb @@ -230,5 +230,76 @@ def private_method; end end end + + describe 'when verify_partial_doubles configuration option is set' do + include_context "with isolated configuration" + + let(:klass) do + Class.new do + def implemented + "works" + end + + def respond_to?(method_name, include_all=false) + method_name.to_s == "dynamic_method" || super + end + + def method_missing(method_name, *args) + if respond_to?(method_name) + method_name + else + super + end + end + end + end + + let(:object) { klass.new } + + before do + RSpec::Mocks.configuration.verify_partial_doubles = true + end + + it 'allows valid methods to be expected' do + expect(object).to receive(:implemented).and_call_original + expect(object.implemented).to eq("works") + end + + it 'does not allow a non-existing method to be expected' do + prevents { expect(object).to receive(:unimplemented) } + end + + it 'verifies arity range when matching arguments' do + prevents { expect(object).to receive(:implemented).with('bogus') } + end + + it 'allows a method defined with method_missing to be expected' do + expect(object).to receive(:dynamic_method).with('a').and_call_original + expect(object.dynamic_method('a')).to eq(:dynamic_method) + end + + it 'allows valid methods to be expected on any_instance' do + expect_any_instance_of(klass).to receive(:implemented) + object.implemented + end + + it 'does not allow a non-existing method to be called on any_instance' do + prevents { expect_any_instance_of(klass).to receive(:unimplemented) } + end + + it 'does not allow missing methods to be called on any_instance' do + # This is potentially surprising behaviour, but there is no way for us + # to know that this method is valid since we only have class and not an + # instance. + prevents { expect_any_instance_of(klass).to receive(:dynamic_method) } + end + + it 'verifies arity range when receiving a message' do + allow(object).to receive(:implemented) + expect { + object.implemented('bogus') + }.to raise_error(ArgumentError, /wrong number of arguments \(1 for 0\)/) + end + end end end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 0be381c57..42ddc47b6 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -28,11 +28,6 @@ class Nested; end module RSpec module Mocks describe 'verifying doubles' do - def prevents(&block) - expect(&block).to \ - raise_error(RSpec::Mocks::MockExpectationError) - end - describe 'instance doubles' do describe 'when doubled class is not loaded' do include_context "with isolated configuration" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f14c8fa3a..9e4752e1d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -42,6 +42,13 @@ def expect_warning_with_call_site(file, line) end end +module VerificationHelpers + def prevents(&block) + expect(&block).to \ + raise_error(RSpec::Mocks::MockExpectationError) + end +end + RSpec.configure do |config| config.mock_with :rspec config.color_enabled = true @@ -65,6 +72,7 @@ def expect_warning_with_call_site(file, line) config.include VerifyAndResetHelpers config.include DeprecationHelpers + config.include VerificationHelpers end shared_context "with syntax" do |syntax| From 11318d9c073c58ca733a28070963294b5bb40bc5 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 30 Oct 2013 19:41:42 -0700 Subject: [PATCH 0281/1179] Replace coveralls with cane. --- .cane | 4 ++++ Gemfile | 9 ++++----- script/test_all | 8 +++++++- spec/spec_helper.rb | 17 ++++++++--------- 4 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 .cane diff --git a/.cane b/.cane new file mode 100644 index 000000000..8b82c68e2 --- /dev/null +++ b/.cane @@ -0,0 +1,4 @@ +--no-doc +--no-style +--no-abc +--gte coverage/.last_run.json,98 diff --git a/Gemfile b/Gemfile index ee4ee1ad7..11e559532 100644 --- a/Gemfile +++ b/Gemfile @@ -18,11 +18,10 @@ group :documentation do gem 'github-markup', '0.7.2' end -### dep for ci/coverage -gem 'coveralls', :require => false - -# mime-types 2 requires ruby 1.8, so we have to specify an old version. -gem 'mime-types', '~> 1.0' +if RUBY_VERSION >= '1.9.3' + gem 'simplecov' + gem 'cane' +end platforms :jruby do gem "jruby-openssl" diff --git a/script/test_all b/script/test_all index 2143261c5..5408d5bb8 100755 --- a/script/test_all +++ b/script/test_all @@ -18,8 +18,14 @@ export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' echo "Running all..." + bin/rspec spec --format progress --profile +if ruby -e 'exit RUBY_VERSION >= "1.9.3" && RUBY_ENGINE == "ruby"' +then + bin/cane # Uses options from .cane +fi + echo echo "--------------------------------------------------------------------" echo @@ -28,7 +34,7 @@ if is_jruby; then echo "Skipping one-by-one spec runs due to expensive JVM load time" else for file in `find spec -iname '*_spec.rb'`; do - NO_COVERALLS=1 bin/rspec $file -b --format progress + NO_COVERAGE=1 bin/rspec $file -b --format progress done fi diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9e4752e1d..4c73b0011 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,15 +4,14 @@ rescue LoadError end -unless ENV['NO_COVERALLS'] || RUBY_VERSION == '1.9.2' - require 'simplecov' if RUBY_VERSION.to_f > 1.8 - require 'coveralls' - Coveralls.wear! do - add_filter '/bundle/' - add_filter '/spec/' - add_filter '/tmp/' - end -end +begin + require 'simplecov' + + SimpleCov.start do + add_filter "bundle" + end +rescue LoadError +end unless ENV['NO_COVERAGE'] || RUBY_VERSION < '1.9.3' RSpec::Matchers.define :include_method do |expected| match do |actual| From c33bdbfd2be896e888df30bae9d41d29f53f0b1a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 31 Oct 2013 21:58:34 -0700 Subject: [PATCH 0282/1179] Remove coveralls badge. [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cb0ba10a2..1e74c4239 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) [![Coverage Status](https://coveralls.io/repos/rspec/rspec-mocks/badge.png?branch=master)](https://coveralls.io/r/rspec/rspec-mocks?branch=master) +# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) rspec-mocks is a test-double framework for rspec with support for method stubs, fakes, and message expectations on generated test-doubles and real objects From 5d6b29cfd57009b02dac0f9d6a7736b153f50dae Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 4 Nov 2013 20:57:20 -0800 Subject: [PATCH 0283/1179] Consistent formatting of changelog. --- Changelog.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Changelog.md b/Changelog.md index 14da506aa..08b8ee7b4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,10 +6,10 @@ Breaking Changes for 3.0.0: * Removed `double`-aliases `stub` and `mock`. (Michi Huber) * Raise an explicit error if `should_not_receive(...).and_return` is used. (Sam Phippen) -* Remove 1.8.6 workarounds (Jon Rowe) +* Remove 1.8.6 workarounds. (Jon Rowe) * Remove `stub!` and `unstub!`. (Sam Phippen) * Remove `mock(name, methods)` and `stub(name, methods)`, - leaving `double(name, methods)` for creating test doubles. (Sam Phippen) + leaving `double(name, methods)` for creating test doubles. (Sam Phippen) * Remove `any_number_of_times` since `should_receive(:msg).any_number_of_times` is really a stub in a mock's clothing. (Sam Phippen) * Remove support for re-using the same null-object test double in multiple @@ -17,20 +17,20 @@ Breaking Changes for 3.0.0: (Myron Marston) * Make `at_least(0)` raise an error. (Sam Phippen) * Remove support for `require 'spec/mocks'` which had been kept - in place for backwards compatibility with rspec 1 (Myron Marston). -* Blocks provided to `with` are always used as implementation (Xavier Shay). + in place for backwards compatibility with RSpec 1. (Myron Marston) +* Blocks provided to `with` are always used as implementation. (Xavier Shay) Enhancements: * Allow the `have_received` matcher to use a block to set further expectations on arguments. (Tim Cowlishaw) -* Yield the receiver to `any_instance` implementation blocks (Sam Phippen). +* Yield the receiver to `any_instance` implementation blocks. (Sam Phippen) * Provide `instance_double` and `class_double` to create verifying doubles, - ported from `rspec-fire` (Xavier Shay). -* `as_null_object` on a verifying double only responds to defined methods - (Xavier Shay). + ported from `rspec-fire`. (Xavier Shay) +* `as_null_object` on a verifying double only responds to defined methods. + (Xavier Shay) * Provide `object_double` to create verified doubles of specific object - instances (Xavier Shay). + instances. (Xavier Shay) * Provide 'verify_partial_doubles` configuration that provides `object_double` like verification behaviour on partial mocks. (Xavier Shay) * Improved performance of double creation, particularly those with many @@ -38,13 +38,13 @@ Enhancements: * Default value of `transfer_nested_constants` option for constant stubbing can be configured. (Xavier Shay) * Messages can be allowed or expected on in bulk via - `receive_messages(:message => :value)` (Jon Rowe) + `receive_messages(:message => :value)`. (Jon Rowe) * `allow(Klass.any_instance)` and `expect(Klass.any_instance)` now print a - warning. This is usually a mistake, and users usually want + warning. This is usually a mistake, and users usually want `allow_any_instance_of` or `expect_any_instance_of` instead. (Sam Phippen) * `instance_double` and `class_double` raise `ArgumentError` if the underlying module is loaded and the arity of the method being invoked does not match the - arity of the method as it is actually implemented. (Andy Lindeman) + arity of the method as it is actually implemented. (Andy Lindeman) * Spies can now check their invocation ordering is correct. (Jon Rowe) Deprecations: From fe93405880f9656b309c67547f899154fda6250f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 5 Nov 2013 07:53:32 -0800 Subject: [PATCH 0284/1179] Remove unnecessary `to_sym`. The message here will always be a symbol because it originates from our `record :message_name` macros in the `Customizations` module. --- lib/rspec/mocks/any_instance/chain.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 16c3350d2..f3290e944 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -70,7 +70,7 @@ def never private def negated? - messages.any? { |(message, *_), _| message.to_sym == :never } + messages.any? { |(message, *_), _| message == :never } end def messages From 6d47442b27624a285f47b138e6dede0f3782b786 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 5 Nov 2013 07:59:29 -0800 Subject: [PATCH 0285/1179] Correct doc string. --- spec/rspec/mocks/any_instance_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 4e2be90e8..f6d948993 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -37,7 +37,7 @@ def private_method; :private_method_return_value; end end context "#stub_chain" do - it "raises an error if 'stub_chain' follows 'any_instance'" do + it "raises an error if 'stub_chain' follows 'and_return'" do expect { klass.any_instance.and_return("1").stub_chain(:foo, :bar) }.to raise_error(NoMethodError) end end From e65b0bfe0624534a0b60155eaf0af99ca2252244 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 5 Nov 2013 08:08:49 -0800 Subject: [PATCH 0286/1179] Make `method_double` private. There's no reason for it to be protected. --- lib/rspec/mocks/proxy.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index e1c5f4df7..643711a86 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -191,14 +191,12 @@ def raise_missing_default_stub_error(expectation, *args) @error_generator.raise_missing_default_stub_error(expectation, *args) end - protected + private def method_double @method_double ||= Hash.new {|h,k| h[k] = MethodDouble.new(@object, k, self) } end - private - def method_doubles method_double.values end From 01110e251f2fb06a7dc4038c442e477b0f6799bd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 5 Nov 2013 07:31:04 -0800 Subject: [PATCH 0287/1179] Add specs for string vs symbol usage. Each of these specs covers a case that was previously uncovered. We use `to_sym` in a bunch of places to normalize message names to a symbol. This is important, but when I removed the `to_sym` calls, nothing was failing. Without the `to_sym` calls, these specs fail, and they also cause subsequent specs to fail because `reset` fails to reset (since it winds up with two message doubles, `:foo` and "foo", and resets the first and blows up on the second since it already got reset -- that leaves the method double around and it is attempted to be reset on reset for subsequent specs). --- spec/rspec/mocks/any_instance_spec.rb | 6 ++++++ spec/rspec/mocks/matchers/receive_spec.rb | 8 ++++++++ spec/rspec/mocks/mock_spec.rb | 19 ++++++++++++++++++ spec/rspec/mocks/stub_chain_spec.rb | 8 ++++++++ .../syntax_agnostic_message_matchers_spec.rb | 20 +++++++++++++++++++ 5 files changed, 61 insertions(+) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index f6d948993..4f948959d 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -334,6 +334,12 @@ class RSpec::SampleRspecTestClass;end klass.any_instance.unstub(:existing_method) }.to raise_error(RSpec::Mocks::MockExpectationError, 'The method `existing_method` was not stubbed or was already unstubbed') end + + it 'does not get confused about string vs symbol usage for the message' do + klass.any_instance.stub(:existing_method) { :stubbed } + klass.any_instance.unstub("existing_method") + expect(klass.new.existing_method).to eq(:existing_method_return_value) + end end context "with #should_not_receive" do diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 7432eaa27..14a74d6de 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -85,6 +85,14 @@ def verify_all wrapped.to eq(3) }.to raise_error(UnsupportedMatcherError) end + + it 'does not get confused by messages being passed as strings and symbols' do + wrapped.to receive(:foo).with(1) { :a } + wrapped.to receive("foo").with(2) { :b } + + expect(receiver.foo(1)).to eq(:a) + expect(receiver.foo(2)).to eq(:b) + end end shared_examples_for "an expect syntax allowance" do |*options| diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb index d20c031d8..4b70880b4 100644 --- a/spec/rspec/mocks/mock_spec.rb +++ b/spec/rspec/mocks/mock_spec.rb @@ -112,6 +112,25 @@ module Mocks verify @double end + it 'does not get confused when `should_not_received` is used with a string and symbol message' do + @double.stub(:foo) { 3 } + @double.should_not_receive(:foo).with(1) + @double.should_not_receive("foo").with(2) + + expect(@double.foo).to eq(3) + verify @double + end + + it 'does not get confused when `should_received` is used with a string and symbol message' do + @double.should_receive(:foo).with(1) + @double.should_receive("foo").with(2) + + @double.foo(1) + @double.foo(2) + + verify @double + end + it "allows block to calculate return values" do @double.should_receive(:something).with("a","b","c").and_return { |a,b,c| c+b+a } expect(@double.something("a","b","c")).to eq "cba" diff --git a/spec/rspec/mocks/stub_chain_spec.rb b/spec/rspec/mocks/stub_chain_spec.rb index 4399fd764..c617bcb70 100644 --- a/spec/rspec/mocks/stub_chain_spec.rb +++ b/spec/rspec/mocks/stub_chain_spec.rb @@ -5,6 +5,14 @@ module Mocks describe "A chained method stub" do let(:object) { Object.new } + it 'does not get confused by symbol vs string usage for the messages' do + object.stub_chain(:foo, :bar => 1) + object.stub_chain("foo", :bazz => 2) + + expect(object.foo.bar).to eq(1) + expect(object.foo.bazz).to eq(2) + end + context "with one method in chain" do context "using and_return" do it "returns expected value from chaining only one method call" do diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index 33b401669..d3e48ae2b 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -35,6 +35,16 @@ module Mocks ::RSpec::Mocks.allow_message(subject, :message) expect { verify subject }.not_to raise_error end + + it 'does not get confused when the string and symbol message form are both used' do + ::RSpec::Mocks.allow_message(subject, :foo).with(1) { :a } + ::RSpec::Mocks.allow_message(subject, "foo").with(2) { :b } + + expect(subject.foo(1)).to eq(:a) + expect(subject.foo(2)).to eq(:b) + + reset subject + end end describe ".expect_message" do @@ -75,6 +85,16 @@ module Mocks verify subject end + it 'does not get confused when the string and symbol message form are both used' do + ::RSpec::Mocks.expect_message(subject, :foo).with(1) + ::RSpec::Mocks.expect_message(subject, "foo").with(2) + + subject.foo(1) + subject.foo(2) + + verify subject + end + end end From cd9a062a1bc7bf2f988e0bc7c1a0cc9938d2deaa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 4 Nov 2013 23:41:47 -0800 Subject: [PATCH 0288/1179] Fix issue with `d = double("foo" => 1); d.stub("foo")`. The latter stub was being converted to a symbol, but the first wasn't, so as a result, we wound up with two method doubles (one for "foo", one for :foo), and when the proxy was reset, things would blow up since it can't reset the same method twice. Our solution here is to centralize the `to_sym` conversion in the `method_double` creation so that we don't have to remember to `to_sym` the message in each place. This allows us to remove `to_sym` from several places. (See the last commit for the specs that give me confidence in removing `to_sym`) --- lib/rspec/mocks.rb | 4 +-- lib/rspec/mocks/proxy.rb | 41 ++++++++++++++---------------- lib/rspec/mocks/syntax.rb | 4 +-- lib/rspec/mocks/verifying_proxy.rb | 22 ++++++++-------- spec/rspec/mocks/double_spec.rb | 7 +++++ 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 03eb0cb4e..96b814716 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -51,7 +51,7 @@ def allow_message(subject, message, opts={}, &block) CallerFilter.first_non_rspec_line } ::RSpec::Mocks.proxy_for(subject). - add_stub(orig_caller, message.to_sym, opts, &block) + add_stub(orig_caller, message, opts, &block) end # Sets a message expectation on `subject`. @@ -70,7 +70,7 @@ def expect_message(subject, message, opts={}, &block) CallerFilter.first_non_rspec_line } ::RSpec::Mocks.proxy_for(subject). - add_message_expectation(orig_caller, message.to_sym, opts, &block) + add_message_expectation(orig_caller, message, opts, &block) end # @api private diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 643711a86..fcc81b866 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -18,6 +18,7 @@ def initialize(object, order_group, name=nil, options={}) @messages_received = [] @options = options @null_object = false + @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) } end # @private @@ -43,7 +44,7 @@ def method_handle_for(message) # @private def add_message_expectation(location, method_name, opts={}, &block) - meth_double = method_double[method_name] + meth_double = method_double_for(method_name) if null_object? && !block meth_double.add_default_stub(@error_generator, @expectation_ordering, location, opts) do @@ -56,12 +57,12 @@ def add_message_expectation(location, method_name, opts={}, &block) # @private def add_simple_expectation(method_name, response, location) - method_double[method_name].add_simple_expectation method_name, response, @error_generator, location + method_double_for(method_name).add_simple_expectation method_name, response, @error_generator, location end # @private def build_expectation(method_name) - meth_double = method_double[method_name] + meth_double = method_double_for(method_name) meth_double.build_expectation( @error_generator, @@ -72,7 +73,7 @@ def build_expectation(method_name) # @private def replay_received_message_on(expectation, &block) expected_method_name = expectation.message - meth_double = method_double[expected_method_name] + meth_double = method_double_for(expected_method_name) if meth_double.expectations.any? @error_generator.raise_expectation_on_mocked_method(expected_method_name) @@ -102,34 +103,34 @@ def check_for_unexpected_arguments(expectation) # @private def add_stub(location, method_name, opts={}, &implementation) - method_double[method_name].add_stub @error_generator, @expectation_ordering, location, opts, &implementation + method_double_for(method_name).add_stub @error_generator, @expectation_ordering, location, opts, &implementation end # @private def add_simple_stub(method_name, response) - method_double[method_name].add_simple_stub method_name, response + method_double_for(method_name).add_simple_stub method_name, response end # @private def remove_stub(method_name) - method_double[method_name].remove_stub + method_double_for(method_name).remove_stub end # @private def remove_single_stub(method_name, stub) - method_double[method_name].remove_single_stub(stub) + method_double_for(method_name).remove_single_stub(stub) end # @private def verify - method_doubles.each {|d| d.verify} + @method_doubles.each_value {|d| d.verify} ensure reset end # @private def reset - method_doubles.each {|d| d.reset} + @method_doubles.each_value {|d| d.reset} @messages_received.clear end @@ -140,7 +141,7 @@ def received_message?(method_name, *args, &block) # @private def has_negative_expectation?(message) - method_double[message].expectations.detect {|expectation| expectation.negative_expectation_for?(message)} + method_double_for(message).expectations.detect {|expectation| expectation.negative_expectation_for?(message)} end # @private @@ -193,12 +194,8 @@ def raise_missing_default_stub_error(expectation, *args) private - def method_double - @method_double ||= Hash.new {|h,k| h[k] = MethodDouble.new(@object, k, self) } - end - - def method_doubles - method_double.values + def method_double_for(message) + @method_doubles[message.to_sym] end def find_matching_expectation(method_name, *args) @@ -216,7 +213,7 @@ def find_almost_matching_expectation(method_name, *args) def find_best_matching_expectation_for(method_name) first_match = nil - method_double[method_name].expectations.each do |expectation| + method_double_for(method_name).expectations.each do |expectation| next unless yield expectation return expectation unless expectation.called_max_times? first_match ||= expectation @@ -226,11 +223,11 @@ def find_best_matching_expectation_for(method_name) end def find_matching_method_stub(method_name, *args) - method_double[method_name].stubs.find {|stub| stub.matches?(method_name, *args)} + method_double_for(method_name).stubs.find {|stub| stub.matches?(method_name, *args)} end def find_almost_matching_stub(method_name, *args) - method_double[method_name].stubs.find {|stub| stub.matches_name_but_not_args(method_name, *args)} + method_double_for(method_name).stubs.find {|stub| stub.matches_name_but_not_args(method_name, *args)} end end @@ -249,13 +246,13 @@ def method_handle_for(message) # @private def add_simple_expectation(method_name, response, location) - method_double[method_name].configure_method + method_double_for(method_name).configure_method super end # @private def add_simple_stub(method_name, response) - method_double[method_name].configure_method + method_double_for(method_name).configure_method super end diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index becef8b0c..5d0dddcc2 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -31,13 +31,13 @@ def self.enable_should(syntax_host = default_should_syntax_host) def should_receive(message, opts={}, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) opts[:expected_from] ||= CallerFilter.first_non_rspec_line - ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block) + ::RSpec::Mocks.expect_message(self, message, opts, &block) end def should_not_receive(message, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) opts = {:expected_from => CallerFilter.first_non_rspec_line} - ::RSpec::Mocks.expect_message(self, message.to_sym, opts, &block).never + ::RSpec::Mocks.expect_message(self, message, opts, &block).never end def stub(message_or_hash, opts={}, &block) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index a934fde21..f8e5de7ac 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -54,13 +54,11 @@ def initialize(object, order_group, name, method_reference_class) @object = object @doubled_module = name @method_reference_class = method_reference_class - end - # A custom method double is required to pass through a way to lookup - # methods to determine their arity. This is only relevant if the doubled - # class is loaded. - def method_double - @method_double ||= Hash.new do |h,k| + # A custom method double is required to pass through a way to lookup + # methods to determine their arity. This is only relevant if the doubled + # class is loaded. + @method_doubles = Hash.new do |h, k| h[k] = VerifyingMethodDouble.new(@object, k, self, method_reference[k]) end end @@ -78,17 +76,17 @@ class VerifyingPartialMockProxy < PartialMockProxy def initialize(object, expectation_ordering) super(object, expectation_ordering) @doubled_module = DirectObjectReference.new(object) - end - # A custom method double is required to pass through a way to lookup - # methods to determine their arity. - def method_double - @method_double ||= Hash.new do |h,k| + # A custom method double is required to pass through a way to lookup + # methods to determine their arity. + @method_doubles = Hash.new do |h, k| h[k] = VerifyingExistingMethodDouble.new(object, k, self) end end - alias_method :method_reference, :method_double + def method_reference + @method_doubles + end end # @api private diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 404144d97..a19fd245a 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -21,4 +21,11 @@ reset dbl expect(dbl.tainted?).to eq(false) end + + it 'does not get string vs symbol messages confused' do + dbl = double("foo" => 1) + allow(dbl).to receive(:foo).and_return(2) + expect(dbl.foo).to eq(2) + expect { reset dbl }.not_to raise_error + end end From b40bdd49932b190451f8389fa21b710a722079f0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 07:44:04 -0800 Subject: [PATCH 0289/1179] Fix "ambiguous first argument" warning. --- spec/rspec/mocks/and_call_original_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 3e3857072..073fc96f9 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -47,7 +47,7 @@ def self.new_instance end it 'warns when you override an existing implementation' do - expect(RSpec).to receive(:warning).with /overriding a previous implementation/ + expect(RSpec).to receive(:warning).with(/overriding a previous implementation/) expect(instance).to receive(:meth_1) { true }.and_call_original instance.meth_1 end From d145ce4a97d1a165fb1d1d033dbba65736b5fcfb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 07:54:42 -0800 Subject: [PATCH 0290/1179] Switch from `include_examples` to `it_behaves_like`. `include_examples` includes the group contents in the current group, and we had some `lets` that were redefining each other, producing these warnings: /Users/myron/code/rspec-dev/repos/rspec-core/lib/rspec/core/memoized_helpers.rb:199: warning: method redefined; discarding old klass /Users/myron/code/rspec-dev/repos/rspec-mocks/spec/rspec/mocks/matchers/receive_spec.rb:233: warning: previous definition of klass was here /Users/myron/code/rspec-dev/repos/rspec-core/lib/rspec/core/memoized_helpers.rb:206: warning: method redefined; discarding old klass /Users/myron/code/rspec-dev/repos/rspec-core/lib/rspec/core/memoized_helpers.rb:206: warning: previous definition of klass was here /Users/myron/code/rspec-dev/repos/rspec-core/lib/rspec/core/memoized_helpers.rb:199: warning: method redefined; discarding old klass /Users/myron/code/rspec-dev/repos/rspec-mocks/spec/rspec/mocks/matchers/receive_spec.rb:260: warning: previous definition of klass was here /Users/myron/code/rspec-dev/repos/rspec-core/lib/rspec/core/memoized_helpers.rb:206: warning: method redefined; discarding old klass /Users/myron/code/rspec-dev/repos/rspec-core/lib/rspec/core/memoized_helpers.rb:206: warning: previous definition of klass was here `it_behaves_like` defines a sub-group that is isolated, and prevents this warning from occurring. --- spec/rspec/mocks/matchers/receive_spec.rb | 35 ++++++++++++----------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 14a74d6de..8b077922d 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -96,7 +96,7 @@ def verify_all end shared_examples_for "an expect syntax allowance" do |*options| - include_examples "a receive matcher", *options + it_behaves_like "a receive matcher", *options it 'does not expect the message to be received' do wrapped.to receive(:foo) @@ -117,7 +117,7 @@ def verify_all end shared_examples_for "an expect syntax expectation" do |*options| - include_examples "a receive matcher", *options + it_behaves_like "a receive matcher", *options it 'sets up a message expectation that passes if the message is received' do wrapped.to receive(:foo) @@ -213,68 +213,69 @@ def receiver.method_missing(*a); end # a poor man's stub... end describe "allow(...).to receive" do - include_examples "an expect syntax allowance" do + it_behaves_like "an expect syntax allowance" do let(:receiver) { double } let(:wrapped) { allow(receiver) } end - include_examples "resets partial mocks cleanly" do + it_behaves_like "resets partial mocks cleanly" do let(:target) { allow(object) } end end describe "allow(...).not_to receive" do - include_examples "an expect syntax negative allowance" do + it_behaves_like "an expect syntax negative allowance" do let(:wrapped) { allow(double) } end end describe "allow_any_instance_of(...).to receive" do - include_examples "an expect syntax allowance" do + it_behaves_like "an expect syntax allowance" do let(:klass) { Class.new } let(:wrapped) { allow_any_instance_of(klass) } let(:receiver) { klass.new } end - include_examples "resets partial mocks of any instance cleanly" do + + it_behaves_like "resets partial mocks of any instance cleanly" do let(:target) { allow_any_instance_of(klass) } end end describe "allow_any_instance_of(...).not_to receive" do - include_examples "an expect syntax negative allowance" do + it_behaves_like "an expect syntax negative allowance" do let(:wrapped) { allow_any_instance_of(Class.new) } end end describe "expect(...).to receive" do - include_examples "an expect syntax expectation", :allow_other_matchers do + it_behaves_like "an expect syntax expectation", :allow_other_matchers do let(:receiver) { double } let(:wrapped) { expect(receiver) } end - include_examples "resets partial mocks cleanly" do + it_behaves_like "resets partial mocks cleanly" do let(:target) { expect(object) } end end describe "expect_any_instance_of(...).to receive" do - include_examples "an expect syntax expectation", :does_not_report_line_num do + it_behaves_like "an expect syntax expectation", :does_not_report_line_num do let(:klass) { Class.new } let(:wrapped) { expect_any_instance_of(klass) } let(:receiver) { klass.new } end - include_examples "resets partial mocks of any instance cleanly" do + it_behaves_like "resets partial mocks of any instance cleanly" do let(:target) { expect_any_instance_of(klass) } end end describe "expect(...).not_to receive" do - include_examples "an expect syntax negative expectation" do + it_behaves_like "an expect syntax negative expectation" do let(:receiver) { double } let(:wrapped) { expect(receiver) } end end describe "expect_any_instance_of(...).not_to receive" do - include_examples "an expect syntax negative expectation" do + it_behaves_like "an expect syntax negative expectation" do let(:klass) { Class.new } let(:wrapped) { expect_any_instance_of(klass) } let(:receiver) { klass.new } @@ -334,7 +335,7 @@ def eq(value) end end - include_examples "using rspec-mocks in another test framework" + it_behaves_like "using rspec-mocks in another test framework" it 'cannot use `expect` with another matcher' do expect { @@ -368,7 +369,7 @@ def eq(value) end end - include_examples "using rspec-mocks in another test framework" + it_behaves_like "using rspec-mocks in another test framework" it 'can use `expect` with any matcher' do framework.new.instance_exec do @@ -390,7 +391,7 @@ def eq(value) end end - include_examples "using rspec-mocks in another test framework" + it_behaves_like "using rspec-mocks in another test framework" it 'can use `expect` with any matcher' do framework.new.instance_exec do From f265472bc4021edaff75aa9336ad1c68142f4c0d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 07:57:19 -0800 Subject: [PATCH 0291/1179] Explicitly enable `should` mocks syntax since we use it here. --- spec/spec_helper.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4c73b0011..5fcb4c4fe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -59,6 +59,13 @@ def prevents(&block) expectations.syntax = :expect end + # TODO: switch most things to the `expect` syntax (and configure only that here) + # but keep a small number of specs that use the old syntax in order to test it + # (using the "with syntax" shared context defined below). + config.mock_with :rspec do |mocks| + mocks.syntax = [:should, :expect] + end + old_verbose = nil config.before(:each, :silence_warnings) do old_verbose = $VERBOSE From d08388aef0806be92fd9dd2050fd8caaf3e3d7b5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 08:27:45 -0800 Subject: [PATCH 0292/1179] Improve deprecation warning. It's helpful to clarify this warning is coming from rspec-mocks given that rspec-expectations issues a similar one. --- lib/rspec/mocks/syntax.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 5d0dddcc2..3d0714c04 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -13,7 +13,7 @@ def self.warn_about_should! def self.warn_unless_should_configured(method_name) if @warn_about_should RSpec.deprecate( - "Using `#{method_name}` from the old `:should` syntax without explicitly enabling the syntax", + "Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax", :replacement => "the new `:expect` syntax or explicitly enable `:should`" ) From 94e7ace495d28b5c34cef2fc54cb26d3301fc925 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 08:32:23 -0800 Subject: [PATCH 0293/1179] Correct message to it refers to the correct config option. --- lib/rspec/mocks/example_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 39faf2394..d31cb01c3 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -181,7 +181,7 @@ def declare_verifying_double(type, ref, *args) raise NameError, "#{ref.name} is not a defined constant. " + "Perhaps you misspelt it? " + - "Disable check with verify_constant_names configuration option." + "Disable check with verify_doubled_constant_names configuration option." end declare_double(type, ref, *args) From ad37d581386da0cc08ad48cb09a8ee8c72bf025d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 11:56:51 -0800 Subject: [PATCH 0294/1179] Update changelog with 2.99.0.beta1 release notes. [ci skip] --- Changelog.md | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 08b8ee7b4..15e0a819d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,13 +3,12 @@ Breaking Changes for 3.0.0: -* Removed `double`-aliases `stub` and `mock`. (Michi Huber) * Raise an explicit error if `should_not_receive(...).and_return` is used. (Sam Phippen) * Remove 1.8.6 workarounds. (Jon Rowe) * Remove `stub!` and `unstub!`. (Sam Phippen) -* Remove `mock(name, methods)` and `stub(name, methods)`, - leaving `double(name, methods)` for creating test doubles. (Sam Phippen) +* Remove `mock(name, methods)` and `stub(name, methods)`, leaving + `double(name, methods)` for creating test doubles. (Sam Phippen, Michi Huber) * Remove `any_number_of_times` since `should_receive(:msg).any_number_of_times` is really a stub in a mock's clothing. (Sam Phippen) * Remove support for re-using the same null-object test double in multiple @@ -19,12 +18,13 @@ Breaking Changes for 3.0.0: * Remove support for `require 'spec/mocks'` which had been kept in place for backwards compatibility with RSpec 1. (Myron Marston) * Blocks provided to `with` are always used as implementation. (Xavier Shay) +* The config option (added in 2.99) to yield the receiver to + `any_instance` implementation blocks now defaults to "on". (Sam Phippen) Enhancements: * Allow the `have_received` matcher to use a block to set further expectations on arguments. (Tim Cowlishaw) -* Yield the receiver to `any_instance` implementation blocks. (Sam Phippen) * Provide `instance_double` and `class_double` to create verifying doubles, ported from `rspec-fire`. (Xavier Shay) * `as_null_object` on a verifying double only responds to defined methods. @@ -62,6 +62,31 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.0.beta1 / 2013-11-07 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.99.0.beta1) + +Deprecations + +* Expecting to use lambdas or other strong arity implementations for stub + methods with mis-matched arity is deprecated and support for them will be + removed in 3.0. Either provide the right amount of arguments or use a weak + arity implementation (methods with splats or procs). (Jon Rowe) +* Using the same test double instance in multiple examples is deprecated. Test + doubles are only meant to live for one example. The mocks and stubs have + always been reset between examples; however, in 2.x the `as_null_object` + state was not reset and some users relied on this to have a null object + double that is used for many examples. This behavior will be removed in 3.0. + (Myron Marston) +* Print a detailed warning when an `any_instance` implementation block is used + when the new `yield_receiver_to_any_instance_implementation_blocks` config + option is not explicitly set, as RSpec 3.0 will default to enabling this new + feature. (Sam Phippen) + +Enhancements: + +* Add a config option to yield the receiver to `any_instance` implementation + blocks. (Sam Phippen) + ### 2.14.4 / 2013-10-15 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.3...v2.14.4) From 30fed6d998f3a74015dc83063fd796be8e6b76d4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 19:41:38 -0800 Subject: [PATCH 0295/1179] Setup gem signing --- Rakefile | 10 ++++++++++ rspec-mocks.gemspec | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/Rakefile b/Rakefile index 090b2c497..a3c88f868 100644 --- a/Rakefile +++ b/Rakefile @@ -71,3 +71,13 @@ task :relish, :version do |t, args| end task :default => [:spec, :cucumber] + +task :verify_private_key_present do + private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') + unless File.exists?(private_key) + raise "Your private key is not present. This gem should not be built without that." + end +end + +task :build => :verify_private_key_present + diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 2ef0830ef..6c70dcc3a 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -23,6 +23,12 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.8.7' + private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') + if File.exists?(private_key) + s.signing_key = private_key + s.cert_chain = [File.expand_path('~/.gem/rspec-gem-public_cert.pem')] + end + if RSpec::Mocks::Version::STRING =~ /[a-zA-Z]+/ # pin to exact version for rc's and betas s.add_runtime_dependency "rspec-support", "= #{RSpec::Mocks::Version::STRING}" From 628fcfbaf9aec599b59ee55519e28a7a27ae3706 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 19:52:21 -0800 Subject: [PATCH 0296/1179] Update changelog for 3.0.0.beta1 release [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 15e0a819d..8741dd152 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### Development -[full changelog](http://github.com/rspec/rspec-mocks/compare/2-99-maintenance...master) +### 3.0.0.beta1 / 2013-11-07 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) Breaking Changes for 3.0.0: From a501f92ae0b92059f4bef892754b2522597453d7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 7 Nov 2013 19:53:41 -0800 Subject: [PATCH 0297/1179] Release 3.0.0.beta1 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 12b02b92b..19b8f4a45 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module Version - STRING = '3.0.0.pre' + STRING = '3.0.0.beta1' end end end From ecad2287a6b399c9e290367d8603c065e94c77f7 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Fri, 8 Nov 2013 16:48:54 +0900 Subject: [PATCH 0298/1179] Correct the entry of should syntax deprecation in changelog It's not disabled, it's deprecated. [ci skip] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 8741dd152..5678b24f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -50,7 +50,7 @@ Enhancements: Deprecations: * Using the old `:should` syntax without explicitly configuring it - is disabled. It will continue to work but will emit a deprecation + is deprecated. It will continue to work but will emit a deprecation warning in RSpec 3 if you do not explicitly enable it. (Sam Phippen) Bug Fixes: From 54f9556330ea2e4a615c6c3a538caf7dccbb682a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 8 Nov 2013 13:05:07 -0800 Subject: [PATCH 0299/1179] Fix typo. [ci skip] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 5678b24f5..48146a476 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,7 +31,7 @@ Enhancements: (Xavier Shay) * Provide `object_double` to create verified doubles of specific object instances. (Xavier Shay) -* Provide 'verify_partial_doubles` configuration that provides `object_double` +* Provide `verify_partial_doubles` configuration that provides `object_double` like verification behaviour on partial mocks. (Xavier Shay) * Improved performance of double creation, particularly those with many attributes. (Xavier Shay) From a3fe04c8f460712b875074f9e4a3c62df3be0419 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 8 Nov 2013 17:30:10 -0800 Subject: [PATCH 0300/1179] Remove dead code. --- spec/rspec/mocks_spec.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 65e0a94d8..83e45e471 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -56,11 +56,4 @@ end end - def file_contents_for(lib, filename) - # http://rubular.com/r/HYpUMftlG2 - path = $LOAD_PATH.find { |p| p.match(/\/rspec-#{lib}(-[a-f0-9]+)?\/lib/) } - file = File.join(path, filename) - File.read(file) - end - end From b38b71fdb65bcbba17f90baced1791c27748f8da Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 8 Nov 2013 20:08:39 -0800 Subject: [PATCH 0301/1179] Squat on less of the user's method namespace. RSpec::Mocks::ExampleMethods is mixed into a context where users freely define their own methods. As such, it's best to not define private helper methods as the user may inadvertently overwrite them. These methods were all stateless, anyway, so they work equally well as class methods and this creates less surface area for override problems. --- lib/rspec/mocks/argument_matchers.rb | 11 +++++------ lib/rspec/mocks/example_methods.rb | 17 +++++++++-------- spec/rspec/mocks/example_methods_spec.rb | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 spec/rspec/mocks/example_methods_spec.rb diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index cbd372319..dd51d4a15 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -203,7 +203,7 @@ def boolean # object.should_receive(:message).with(hash_including(:key)) # object.should_receive(:message).with(hash_including(:key, :key2 => val2)) def hash_including(*args) - HashIncludingMatcher.new(anythingize_lonely_keys(*args)) + HashIncludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args)) end # Matches an array that includes the specified items at least once. @@ -226,7 +226,7 @@ def array_including(*args) # object.should_receive(:message).with(hash_excluding(:key)) # object.should_receive(:message).with(hash_excluding(:key, :key2 => :val2)) def hash_excluding(*args) - HashExcludingMatcher.new(anythingize_lonely_keys(*args)) + HashExcludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args)) end alias_method :hash_not_including, :hash_excluding @@ -252,11 +252,10 @@ def kind_of(klass) alias_method :a_kind_of, :kind_of - private - - def anythingize_lonely_keys(*args) + # @api private + def self.anythingize_lonely_keys(*args) hash = args.last.class == Hash ? args.delete_at(-1) : {} - args.each { | arg | hash[arg] = anything } + args.each { | arg | hash[arg] = AnyArgMatcher.new(nil) } hash end end diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index d31cb01c3..d9e46d40f 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -28,7 +28,7 @@ module ExampleMethods # card.rank #=> "A" # def double(*args) - declare_double(Mock, *args) + ExampleMethods.declare_double(Mock, *args) end # @overload instance_double(doubled_class) @@ -43,7 +43,7 @@ def double(*args) # [double](double). def instance_double(doubled_class, *args) ref = ObjectReference.for(doubled_class) - declare_verifying_double(InstanceVerifyingDouble, ref, *args) + ExampleMethods.declare_verifying_double(InstanceVerifyingDouble, ref, *args) end # @overload class_double(doubled_class) @@ -58,7 +58,7 @@ def instance_double(doubled_class, *args) # [double](double). def class_double(doubled_class, *args) ref = ObjectReference.for(doubled_class) - declare_verifying_double(ClassVerifyingDouble, ref, *args) + ExampleMethods.declare_verifying_double(ClassVerifyingDouble, ref, *args) end # @overload object_double(object_or_name) @@ -73,7 +73,7 @@ def class_double(doubled_class, *args) # for verification. In all other ways it behaves like a [double](double). def object_double(object_or_name, *args) ref = ObjectReference.for(object_or_name, :allow_direct_object_refs) - declare_verifying_double(ObjectVerifyingDouble, ref, *args) + ExampleMethods.declare_verifying_double(ObjectVerifyingDouble, ref, *args) end # Disables warning messages about expectations being set on nil. @@ -164,6 +164,7 @@ def have_received(method_name, &block) Matchers::HaveReceived.new(method_name, &block) end + # @api private def self.included(klass) klass.class_exec do # This gets mixed in so that if `RSpec::Matchers` is included in @@ -172,9 +173,8 @@ def self.included(klass) end end - private - - def declare_verifying_double(type, ref, *args) + # @api private + def self.declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? @@ -187,7 +187,8 @@ def declare_verifying_double(type, ref, *args) declare_double(type, ref, *args) end - def declare_double(type, *args) + # @api private + def self.declare_double(type, *args) args << {} unless Hash === args.last type.new(*args) end diff --git a/spec/rspec/mocks/example_methods_spec.rb b/spec/rspec/mocks/example_methods_spec.rb new file mode 100644 index 000000000..da77ecdd3 --- /dev/null +++ b/spec/rspec/mocks/example_methods_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +module RSpec + module Mocks + describe ExampleMethods do + it 'does not define private helper methods since it gets included into a ' + + 'namespace where users define methods and could inadvertently overwrite ' + + 'them' do + expect(ExampleMethods.private_instance_methods).to eq([]) + end + end + end +end + From 2023a2b8eb7b92a15c2a14916203fe86c0aff75a Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 9 Nov 2013 07:00:15 -0800 Subject: [PATCH 0302/1179] Clarify verifying double documentation. * Moves introduction to README so it shows on the index page. * Made intro text more generic to account for object doubles. * Rename old introduction feature to "instance doubles" for symmetry with class and object double features. --- features/.nav | 2 +- features/verifying_doubles/README.md | 17 ++++++++++++++ ...ction.feature => instance_doubles.feature} | 22 +++++-------------- 3 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 features/verifying_doubles/README.md rename features/verifying_doubles/{introduction.feature => instance_doubles.feature} (67%) diff --git a/features/.nav b/features/.nav index 1ca3a0688..32f640c0a 100644 --- a/features/.nav +++ b/features/.nav @@ -23,7 +23,7 @@ - stub_undefined_constant.feature - hiding_defined_constant.feature - verifying_doubles: - - introduction.feature + - instance_doubles.feature - class_doubles.feature - object_doubles.feature - dynamic_classes.feature diff --git a/features/verifying_doubles/README.md b/features/verifying_doubles/README.md new file mode 100644 index 000000000..945369ade --- /dev/null +++ b/features/verifying_doubles/README.md @@ -0,0 +1,17 @@ +## Verifying Doubles + +Verifying doubles are a stricter alternative to normal doubles that provide +guarantees about what is being verified. When using verifying doubles, RSpec +will check that the methods being stubbed are actually present on the +underlying object if it is available. Prefer using veryifing doubles over +normal doubles. + +No checking will happen if the underlying object or calss is not defined, but +when run with it present (either as a full spec run or by explicitly preloading +collaborators) a failure will be triggered if an invalid method is being +stubbed. + +This dual approach allows you to move very quickly and test components in +isolation, while giving you confidence that your doubles are not a complete +fiction. Testing in isolation is optional but recommend for classes that do not +depend on third-party components. diff --git a/features/verifying_doubles/introduction.feature b/features/verifying_doubles/instance_doubles.feature similarity index 67% rename from features/verifying_doubles/introduction.feature rename to features/verifying_doubles/instance_doubles.feature index 863a2bdfc..c3b74cd80 100644 --- a/features/verifying_doubles/introduction.feature +++ b/features/verifying_doubles/instance_doubles.feature @@ -1,20 +1,8 @@ -Feature: Verifying doubles - - Verifying doubles are a stricter alternative to normal doubles that provide - guarantees about what is being verified. When using verifying doubles, RSpec - will check that the methods being stubbed are actually present on the - underlying object if it is available. Prefer using veryifing doubles over - normal doubles. - - No checking will happen if the constant name is not defined, but when run - with the constant present (either as a full spec run or by explicitly - preloading collaborators) a failure will be triggered if an invalid method is - being stubbed. - - This dual approach allows you to move very quickly and test components in - isolation, while giving you confidence that your doubles are not a complete - fiction. Testing in isolation is optional but recommend for classes that do - not depend on third-party components. +Feature: Using an instance double + + An `instance_double` is the most common type of verifying double. It takes a + class name as its first argument, then verifies that any methods being + stubbed would be present on an _instance_ of that class. Background: Given a file named "app/models/user.rb" with: From 3430684bd0850303dd81aee8a47ecfee1fa87c5a Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 9 Nov 2013 07:27:07 -0800 Subject: [PATCH 0303/1179] Allow string representations of methods in double constructor. --- lib/rspec/mocks/message_expectation.rb | 4 ++-- spec/rspec/mocks/double_spec.rb | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index ce4bfc586..15895010c 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -8,7 +8,7 @@ module Mocks class SimpleMessageExpectation def initialize(message, response, error_generator, backtrace_line = nil) - @message, @response, @error_generator, @backtrace_line = message, response, error_generator, backtrace_line + @message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line @received = false end @@ -18,7 +18,7 @@ def invoke(*_) end def matches?(message, *_) - @message == message + @message == message.to_sym end def called_max_times? diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index a19fd245a..8e2f440fd 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -28,4 +28,9 @@ expect(dbl.foo).to eq(2) expect { reset dbl }.not_to raise_error end + + it 'allows string representation of methods in constructor' do + dbl = double('foo' => 1) + expect(dbl.foo).to eq(1) + end end From 8e484255f3f9769c1cdc12ffa3729d96b839cb2f Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 9 Nov 2013 07:28:41 -0800 Subject: [PATCH 0304/1179] Document behaviour of doubling setter methods. --- spec/rspec/mocks/double_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 8e2f440fd..e5d729dc7 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -33,4 +33,12 @@ dbl = double('foo' => 1) expect(dbl.foo).to eq(1) end + + it 'allows setter methods to be stubbed' do + dbl = double('foo=' => 1) + + # Note the specified return value is thrown away. This is a Ruby semantics + # thing. You cannot change the return value of assignment. + expect(dbl.foo = "bar").to eq("bar") + end end From 5c964920042cf4bb77d9b5abd555a1a999c4b129 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 9 Nov 2013 10:25:40 -0800 Subject: [PATCH 0305/1179] Copy changes and extra link, addressing feedback. --- features/verifying_doubles/README.md | 4 ++-- features/verifying_doubles/instance_doubles.feature | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/features/verifying_doubles/README.md b/features/verifying_doubles/README.md index 945369ade..491f6ec9f 100644 --- a/features/verifying_doubles/README.md +++ b/features/verifying_doubles/README.md @@ -6,10 +6,10 @@ will check that the methods being stubbed are actually present on the underlying object if it is available. Prefer using veryifing doubles over normal doubles. -No checking will happen if the underlying object or calss is not defined, but +No checking will happen if the underlying object or class is not defined, but when run with it present (either as a full spec run or by explicitly preloading collaborators) a failure will be triggered if an invalid method is being -stubbed. +stubbed or a method is called with an invalid number of arguments. This dual approach allows you to move very quickly and test components in isolation, while giving you confidence that your doubles are not a complete diff --git a/features/verifying_doubles/instance_doubles.feature b/features/verifying_doubles/instance_doubles.feature index c3b74cd80..11ee282dc 100644 --- a/features/verifying_doubles/instance_doubles.feature +++ b/features/verifying_doubles/instance_doubles.feature @@ -1,8 +1,11 @@ Feature: Using an instance double An `instance_double` is the most common type of verifying double. It takes a - class name as its first argument, then verifies that any methods being - stubbed would be present on an _instance_ of that class. + class name or object as its first argument, then verifies that any methods + being stubbed would be present on an _instance_ of that class. + + For methods handled by `method_missing`, see [dynamic + objects](./dynamic-objects). Background: Given a file named "app/models/user.rb" with: From 82e33f77bbfc5a718922652b4a0e52ab2b6f19c8 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 9 Nov 2013 10:38:16 -0800 Subject: [PATCH 0306/1179] Flesh out instance double docs. --- .../verifying_doubles/class_doubles.feature | 17 +------ .../instance_doubles.feature | 45 +++++++++++++++---- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/features/verifying_doubles/class_doubles.feature b/features/verifying_doubles/class_doubles.feature index 6dc2cfdde..084e0bbf2 100644 --- a/features/verifying_doubles/class_doubles.feature +++ b/features/verifying_doubles/class_doubles.feature @@ -1,7 +1,7 @@ Feature: Using a class double `class_double` is provided as a complement to `instance_double`, with the - difference that it verifies class methods on the given class rather than + difference that it verifies _class_ methods on the given class rather than instance methods. In addition, it also provides a convenience method `as_stubbed_const` to @@ -71,18 +71,3 @@ Feature: Using a class double When I run `rspec spec/user_spec.rb` Then the output should contain "1 example, 1 failure" And the output should contain "ConsoleNotifier does not implement:" - - Scenario: adding `color` as a second argument to `ConsoleNotifier.notify` - Given a file named "lib/console_notifier.rb" with: - """ruby - class ConsoleNotifier - MAX_WIDTH = 80 - - def self.notify(message, color) - puts color + message - end - end - """ - When I run `rspec spec/user_spec.rb` - Then the output should contain "1 example, 1 failure" - And the output should contain "Wrong number of arguments." diff --git a/features/verifying_doubles/instance_doubles.feature b/features/verifying_doubles/instance_doubles.feature index 11ee282dc..167b0690d 100644 --- a/features/verifying_doubles/instance_doubles.feature +++ b/features/verifying_doubles/instance_doubles.feature @@ -2,7 +2,9 @@ Feature: Using an instance double An `instance_double` is the most common type of verifying double. It takes a class name or object as its first argument, then verifies that any methods - being stubbed would be present on an _instance_ of that class. + being stubbed would be present on an _instance_ of that class. If any + argument matchers are specified, it also verifies that the number of + arguments is correct. For methods handled by `method_missing`, see [dynamic objects](./dynamic-objects). @@ -17,13 +19,6 @@ Feature: Using an instance double end """ - Given a file named "app/models/console_notifier.rb" with: - """ruby - class ConsoleNotifier - # notify is not defined yet. - end - """ - Given a file named "spec/unit_helper.rb" with: """ruby $LOAD_PATH.unshift("app/models") @@ -71,6 +66,38 @@ Feature: Using an instance double When I run `rspec spec/unit/user_spec.rb` Then the examples should all pass - Scenario: spec fails with dependencies loaded + Scenario: spec passes with dependencies loaded and method implemented + Given a file named "app/models/console_notifier.rb" with: + """ruby + class ConsoleNotifier + def notify(msg) + puts message + end + end + """ + + When I run `rspec spec/unit/user_spec.rb` + Then the examples should all pass + + Scenario: spec fails with dependencies loaded and method unimplemented + Given a file named "app/models/console_notifier.rb" with: + """ruby + class ConsoleNotifier + end + """ + When I run `rspec -r./spec/spec_helper spec/unit/user_spec.rb` + Then the output should contain "1 example, 1 failure" + And the output should contain "ConsoleNotifier does not implement:" + + Scenario: spec fails with dependencies loaded and incorrect arity + Given a file named "app/models/console_notifier.rb" with: + """ruby + class ConsoleNotifier + def notify(msg, color) + puts color + message + end + end + """ When I run `rspec -r./spec/spec_helper spec/unit/user_spec.rb` Then the output should contain "1 example, 1 failure" + And the output should contain "Wrong number of arguments." From 86abf8de6f6a23a31eeb2774bd9f3cb2c48b830d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 9 Nov 2013 13:55:14 -0800 Subject: [PATCH 0307/1179] Fixup README code formatting. - Spaces between method args. - A space between `#` and the comment text. --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1e74c4239..f10f6ab9d 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ This is particularly nice when providing a list of test doubles to a method that iterates through them: ```ruby -order.calculate_total_price(double(:price => 1.99),double(:price => 2.99)) +order.calculate_total_price(double(:price => 1.99), double(:price => 2.99)) ``` ## Consecutive return values @@ -59,7 +59,7 @@ arguments to `and_return`. The invocations cycle through the list. The last value is returned for any subsequent invocations: ```ruby -allow(die).to receive(:roll).and_return(1,2,3) +allow(die).to receive(:roll).and_return(1, 2, 3) die.roll # => 1 die.roll # => 2 die.roll # => 3 @@ -206,7 +206,7 @@ expect(double).to receive(:msg).any_number_of_times ```ruby expect(double).to receive(:msg).ordered expect(double).to receive(:other_msg).ordered - #This will fail if the messages are received out of order + # This will fail if the messages are received out of order ``` This can include the same message with different arguments: @@ -234,11 +234,11 @@ expect(double).to receive(:msg).and_return(value) expect(double).to receive(:msg).exactly(3).times.and_return(value1, value2, value3) # returns value1 the first time, value2 the second, etc expect(double).to receive(:msg).and_raise(error) - #error can be an instantiated object or a class - #if it is a class, it must be instantiable with no args + # error can be an instantiated object or a class + # if it is a class, it must be instantiable with no args expect(double).to receive(:msg).and_throw(:msg) -expect(double).to receive(:msg).and_yield(values,to,yield) -expect(double).to receive(:msg).and_yield(values,to,yield).and_yield(some,other,values,this,time) +expect(double).to receive(:msg).and_yield(values, to, yield) +expect(double).to receive(:msg).and_yield(values, to, yield).and_yield(some, other, values, this, time) # for methods that yield to a block multiple times ``` @@ -249,8 +249,8 @@ allow(double).to receive(:msg).and_return(value) allow(double).to receive(:msg).and_return(value1, value2, value3) allow(double).to receive(:msg).and_raise(error) allow(double).to receive(:msg).and_throw(:msg) -allow(double).to receive(:msg).and_yield(values,to,yield) -allow(double).to receive(:msg).and_yield(values,to,yield).and_yield(some,other,values,this,time) +allow(double).to receive(:msg).and_yield(values, to, yield) +allow(double).to receive(:msg).and_yield(values, to, yield).and_yield(some, other, values, this, time) ``` ## Arbitrary Handling From ef4aa3bd25a93ec29e762071b50e01e72c43a697 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 9 Nov 2013 11:49:56 -0800 Subject: [PATCH 0308/1179] Rename `Mock` to `Double` in class names, etc. We've already removed `mock` as an alias for `double`, might as well make class names and descriptions consistent. --- README.md | 2 +- lib/rspec/mocks/example_methods.rb | 6 +- lib/rspec/mocks/framework.rb | 1 - lib/rspec/mocks/mock.rb | 7 - lib/rspec/mocks/proxy.rb | 2 +- lib/rspec/mocks/space.rb | 4 +- lib/rspec/mocks/stub_chain.rb | 2 +- lib/rspec/mocks/test_double.rb | 10 +- lib/rspec/mocks/verifying_double.rb | 1 - lib/rspec/mocks/verifying_proxy.rb | 2 +- spec/rspec/mocks/and_yield_spec.rb | 2 +- spec/rspec/mocks/double_spec.rb | 849 +++++++++++++++++- spec/rspec/mocks/mock_spec.rb | 788 ---------------- .../rspec/mocks/multiple_return_value_spec.rb | 4 +- spec/rspec/mocks/options_hash_spec.rb | 6 +- ...al_mock_spec.rb => partial_double_spec.rb} | 2 +- ...rtial_double_using_mocks_directly_spec.rb} | 2 +- spec/rspec/mocks/space_spec.rb | 2 +- spec/rspec/mocks_spec.rb | 6 +- 19 files changed, 844 insertions(+), 854 deletions(-) delete mode 100644 lib/rspec/mocks/mock.rb delete mode 100644 spec/rspec/mocks/mock_spec.rb rename spec/rspec/mocks/{partial_mock_spec.rb => partial_double_spec.rb} (99%) rename spec/rspec/mocks/{partial_mock_using_mocks_directly_spec.rb => partial_double_using_mocks_directly_spec.rb} (98%) diff --git a/README.md b/README.md index f10f6ab9d..56cf87720 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ generic kind of object: a Test Double. ### Test-Specific Extension -a.k.a. Partial Stub/Mock, a Test-Specific Extension is an extension of a +a.k.a. Partial Double, a Test-Specific Extension is an extension of a real object in a system that is instrumented with test-double like behaviour in the context of a test. This technique is very common in Ruby because we often see class objects acting as global namespaces for methods. diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index d9e46d40f..4448be4a8 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -12,9 +12,9 @@ module ExampleMethods # @param name [String/Symbol] (optional) used in # clarify intent # @param stubs (Hash) (optional) hash of message/return-value pairs - # @return (Mock) + # @return (Double) # - # Constructs an instance of [RSpec::Mocks::Mock](RSpec::Mocks::Mock) configured + # Constructs an instance of [RSpec::Mocks::Double](RSpec::Mocks::Double) configured # with an optional name, used for reporting in failure messages, and an optional # hash of message/return-value pairs. # @@ -28,7 +28,7 @@ module ExampleMethods # card.rank #=> "A" # def double(*args) - ExampleMethods.declare_double(Mock, *args) + ExampleMethods.declare_double(Double, *args) end # @overload instance_double(doubled_class) diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index d7b740b6f..2a38b85bb 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -11,7 +11,6 @@ require 'rspec/mocks/proxy' require 'rspec/mocks/proxy_for_nil' require 'rspec/mocks/test_double' -require 'rspec/mocks/mock' require 'rspec/mocks/argument_list_matcher' require 'rspec/mocks/message_expectation' require 'rspec/mocks/order_group' diff --git a/lib/rspec/mocks/mock.rb b/lib/rspec/mocks/mock.rb deleted file mode 100644 index 4789a1f34..000000000 --- a/lib/rspec/mocks/mock.rb +++ /dev/null @@ -1,7 +0,0 @@ -module RSpec - module Mocks - class Mock - include TestDouble - end - end -end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index fcc81b866..b169735a7 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -231,7 +231,7 @@ def find_almost_matching_stub(method_name, *args) end end - class PartialMockProxy < Proxy + class PartialDoubleProxy < Proxy def method_handle_for(message) if any_instance_class_recorder_observing_method?(@object.class, message) message = ::RSpec::Mocks. diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index e7b8f04ae..3e60bd774 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -58,9 +58,9 @@ def proxy_for(object) when TestDouble then object.__build_mock_proxy(expectation_ordering) else if RSpec::Mocks.configuration.verify_partial_doubles? - VerifyingPartialMockProxy.new(object, expectation_ordering) + VerifyingPartialDoubleProxy.new(object, expectation_ordering) else - PartialMockProxy.new(object, expectation_ordering) + PartialDoubleProxy.new(object, expectation_ordering) end end end diff --git a/lib/rspec/mocks/stub_chain.rb b/lib/rspec/mocks/stub_chain.rb index 99dc01e3e..e45e0b8d1 100644 --- a/lib/rspec/mocks/stub_chain.rb +++ b/lib/rspec/mocks/stub_chain.rb @@ -19,7 +19,7 @@ def stub_chain chain.shift matching_stub.invoke(nil).stub_chain(*chain, &block) else - next_in_chain = Mock.new + next_in_chain = Double.new object.stub(chain.shift) { next_in_chain } StubChain.stub_chain_on(next_in_chain, *chain, &block) end diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 1cc9b58f7..a095a380b 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -1,8 +1,8 @@ module RSpec module Mocks - # Implements the methods needed for a pure test double. RSpec::Mocks::Mock + # Implements the methods needed for a pure test double. RSpec::Mocks::Double # includes this module, and it is provided for cases where you want a - # pure test double without subclassing RSpec::Mocks::Mock. + # pure test double without subclassing RSpec::Mocks::Double. module TestDouble # Extends the TestDouble module onto the given object and # initializes it as a test double. @@ -105,5 +105,11 @@ def __mock_proxy ::RSpec::Mocks.proxy_for(self) end end + + # A generic test double object. `double`, `instance_double` and friends + # return an instance of this. + class Double + include TestDouble + end end end diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 785be3ff3..6087fe37b 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -1,4 +1,3 @@ -require 'rspec/mocks/mock' require 'rspec/mocks/verifying_proxy' module RSpec diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index f8e5de7ac..cb148a926 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -70,7 +70,7 @@ def method_reference end end - class VerifyingPartialMockProxy < PartialMockProxy + class VerifyingPartialDoubleProxy < PartialDoubleProxy include VerifyingProxyMethods def initialize(object, expectation_ordering) diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index dd72346ea..3e0a0761f 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe RSpec::Mocks::Mock do +describe RSpec::Mocks::Double do let(:obj) { double } diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index e5d729dc7..98643db25 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -1,44 +1,825 @@ -require "spec_helper" +require 'spec_helper' -describe "double" do - it "is an alias for stub and mock" do - expect(double()).to be_a(RSpec::Mocks::Mock) - end +module RSpec + module Mocks + describe Double do + before(:each) { @double = double("test double") } + after(:each) { reset @double } - it "uses 'Double' in failure messages" do - double = double('name') - expect {double.foo}.to raise_error(/Double "name" received/) - end + it "has method_missing as private" do + expect(RSpec::Mocks::Double.private_instance_methods).to include_method(:method_missing) + end - it "hides internals in its inspect representation" do - m = double('cup') - expect(m.inspect).to match(/#/) - end + it "does not respond_to? method_missing (because it's private)" do + expect(RSpec::Mocks::Double.new).not_to respond_to(:method_missing) + end - it 'restores standard object methods on reset' do - dbl = double(:tainted? => true) - expect(dbl.tainted?).to eq(true) - reset dbl - expect(dbl.tainted?).to eq(false) - end + it "uses 'Double' in failure messages" do + double = double('name') + expect { double.foo }.to raise_error(/Double "name" received/) + end - it 'does not get string vs symbol messages confused' do - dbl = double("foo" => 1) - allow(dbl).to receive(:foo).and_return(2) - expect(dbl.foo).to eq(2) - expect { reset dbl }.not_to raise_error - end + it "hides internals in its inspect representation" do + m = double('cup') + expect(m.inspect).to match(/#/) + end - it 'allows string representation of methods in constructor' do - dbl = double('foo' => 1) - expect(dbl.foo).to eq(1) - end + it 'restores standard object methods on reset' do + dbl = double(:tainted? => true) + expect(dbl.tainted?).to eq(true) + reset dbl + expect(dbl.tainted?).to eq(false) + end + + it 'does not get string vs symbol messages confused' do + dbl = double("foo" => 1) + allow(dbl).to receive(:foo).and_return(2) + expect(dbl.foo).to eq(2) + expect { reset dbl }.not_to raise_error + end + + it 'allows string representation of methods in constructor' do + dbl = double('foo' => 1) + expect(dbl.foo).to eq(1) + end + + it 'allows setter methods to be stubbed' do + dbl = double('foo=' => 1) + + # Note the specified return value is thrown away. This is a Ruby semantics + # thing. You cannot change the return value of assignment. + expect(dbl.foo = "bar").to eq("bar") + end + + it "reports line number of expectation of unreceived message" do + expected_error_line = __LINE__; @double.should_receive(:wont_happen).with("x", 3) + begin + verify @double + violated + rescue RSpec::Mocks::MockExpectationError => e + # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line + expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) + end + end + + it "reports line number of expectation of unreceived message after #should_receive after similar stub" do + @double.stub(:wont_happen) + expected_error_line = __LINE__; @double.should_receive(:wont_happen).with("x", 3) + begin + verify @double + violated + rescue RSpec::Mocks::MockExpectationError => e + # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line + expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) + end + end + + it "passes when not receiving message specified as not to be received" do + @double.should_not_receive(:not_expected) + verify @double + end + + it "prevents confusing double-negative expressions involving `never`" do + expect { + @double.should_not_receive(:not_expected).never + }.to raise_error(/trying to negate it again/) + end + + it "warns when `and_return` is called on a negative expectation" do + expect { + @double.should_not_receive(:do_something).and_return(1) + }.to raise_error(/not supported/) + + expect { + expect(@double).not_to receive(:do_something).and_return(1) + }.to raise_error(/not supported/) + + expect { + expect(@double).to receive(:do_something).never.and_return(1) + }.to raise_error(/not supported/) + end + + it "passes when receiving message specified as not to be received with different args" do + @double.should_not_receive(:message).with("unwanted text") + @double.should_receive(:message).with("other text") + @double.message "other text" + verify @double + end + + it "fails when receiving message specified as not to be received" do + @double.should_not_receive(:not_expected) + expect { + @double.not_expected + violated + }.to raise_error( + RSpec::Mocks::MockExpectationError, + %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| + ) + end + + it "fails when receiving message specified as not to be received with args" do + @double.should_not_receive(:not_expected).with("unexpected text") + expect { + @double.not_expected("unexpected text") + violated + }.to raise_error( + RSpec::Mocks::MockExpectationError, + %Q|(Double "test double").not_expected("unexpected text")\n expected: 0 times with arguments: ("unexpected text")\n received: 1 time with arguments: ("unexpected text")| + ) + end + + it "fails when array arguments do not match" do + @double.should_not_receive(:not_expected).with(["do not want"]) + expect { + @double.not_expected(["do not want"]) + violated + }.to raise_error( + RSpec::Mocks::MockExpectationError, + %Q|(Double "test double").not_expected(["do not want"])\n expected: 0 times with arguments: (["do not want"])\n received: 1 time with arguments: (["do not want"])| + ) + end + + it "passes when receiving message specified as not to be received with wrong args" do + @double.should_not_receive(:not_expected).with("unexpected text") + @double.not_expected "really unexpected text" + verify @double + + @double.should_receive(:not_expected).with("unexpected text").never + @double.not_expected "really unexpected text" + verify @double + end + + it 'does not get confused when `should_not_received` is used with a string and symbol message' do + @double.stub(:foo) { 3 } + @double.should_not_receive(:foo).with(1) + @double.should_not_receive("foo").with(2) + + expect(@double.foo).to eq(3) + verify @double + end + + it 'does not get confused when `should_received` is used with a string and symbol message' do + @double.should_receive(:foo).with(1) + @double.should_receive("foo").with(2) + + @double.foo(1) + @double.foo(2) + + verify @double + end + + it "allows block to calculate return values" do + @double.should_receive(:something).with("a","b","c").and_return { |a,b,c| c+b+a } + expect(@double.something("a","b","c")).to eq "cba" + verify @double + end + + it "allows parameter as return value" do + @double.should_receive(:something).with("a","b","c").and_return("booh") + expect(@double.something("a","b","c")).to eq "booh" + verify @double + end + + it "returns the previously stubbed value if no return value is set" do + @double.stub(:something).with("a","b","c").and_return(:stubbed_value) + @double.should_receive(:something).with("a","b","c") + expect(@double.something("a","b","c")).to eq :stubbed_value + verify @double + end + + it "returns nil if no return value is set and there is no previously stubbed value" do + @double.should_receive(:something).with("a","b","c") + expect(@double.something("a","b","c")).to be_nil + verify @double + end + + it "raises exception if args don't match when method called" do + @double.should_receive(:something).with("a","b","c").and_return("booh") + expect { + @double.something("a","d","c") + violated + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") + end + + describe "even when a similar expectation with different arguments exist" do + it "raises exception if args don't match when method called, correctly reporting the offending arguments" do + @double.should_receive(:something).with("a","b","c").once + @double.should_receive(:something).with("z","x","c").once + expect { + @double.something("a","b","c") + @double.something("z","x","g") + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"z\", \"x\", \"c\")\n got: (\"z\", \"x\", \"g\")") + end + end + + it "raises exception if args don't match when method called even when the method is stubbed" do + @double.stub(:something) + @double.should_receive(:something).with("a","b","c") + expect { + @double.something("a","d","c") + verify @double + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") + end + + it "raises exception if args don't match when method called even when using null_object" do + @double = double("test double").as_null_object + @double.should_receive(:something).with("a","b","c") + expect { + @double.something("a","d","c") + verify @double + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") + end + + describe 'with a method that has a default argument' do + it "raises an exception if the arguments don't match when the method is called, correctly reporting the offending arguments" do + def @double.method_with_default_argument(arg={}); end + @double.should_receive(:method_with_default_argument).with({}) + + expect { + @double.method_with_default_argument(nil) + verify @double + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)") + end + end + + it "fails if unexpected method called" do + expect { + @double.something("a","b","c") + violated + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received unexpected message :something with (\"a\", \"b\", \"c\")") + end + + it "uses block for expectation if provided" do + @double.should_receive(:something) do | a, b | + expect(a).to eq "a" + expect(b).to eq "b" + "booh" + end + expect(@double.something("a", "b")).to eq "booh" + verify @double + end + + it "fails if expectation block fails" do + @double.should_receive(:something) do |bool| + expect(bool).to be_truthy + end + + expect { + @double.something false + }.to raise_error(RSpec::Expectations::ExpectationNotMetError) + end + + it "is wrappable in an array" do + expect( Array(@double) ).to eq([@double]) + end + + it "is wrappable in an array when a null object" do + expect( Array(@double.as_null_object) ).to eq [@double] + end + + it "responds to to_ary as a null object" do + expect(@double.as_null_object.to_ary).to eq nil + end + + it "responds to to_a as a null object" do + if RUBY_VERSION.to_f > 1.8 + expect(@double.as_null_object.to_a).to eq nil + else + expect(@double.as_null_object.to_a).to eq [@double] + end + end + + it "passes proc to expectation block without an argument" do + @double.should_receive(:foo) { |&block| expect(block.call).to eq(:bar) } + @double.foo { :bar } + end + + it "passes proc to expectation block with an argument" do + @double.should_receive(:foo) { |arg, &block| expect(block.call).to eq(:bar) } + @double.foo(:arg) { :bar } + end + + it "passes proc to stub block without an argurment" do + @double.stub(:foo) { |&block| expect(block.call).to eq(:bar) } + @double.foo { :bar } + end + + it "passes proc to stub block with an argument" do + @double.stub(:foo) { |arg, &block| expect(block.call).to eq(:bar) } + @double.foo(:arg) { :bar } + end + + it "fails right away when method defined as never is received" do + @double.should_receive(:not_expected).never + expect { @double.not_expected }. + to raise_error(RSpec::Mocks::MockExpectationError, + %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| + ) + end + + it "raises RuntimeError by default" do + @double.should_receive(:something).and_raise + expect { @double.something }.to raise_error(RuntimeError) + end + + it "raises RuntimeError with a message by default" do + @double.should_receive(:something).and_raise("error message") + expect { @double.something }.to raise_error(RuntimeError, "error message") + end + + it "raises an exception of a given type without an error message" do + @double.should_receive(:something).and_raise(StandardError) + expect { @double.something }.to raise_error(StandardError) + end + + it "raises an exception of a given type with a message" do + @double.should_receive(:something).and_raise(RuntimeError, "error message") + expect { @double.something }.to raise_error(RuntimeError, "error message") + end + + it "raises a given instance of an exception" do + @double.should_receive(:something).and_raise(RuntimeError.new("error message")) + expect { @double.something }.to raise_error(RuntimeError, "error message") + end + + class OutOfGas < StandardError + attr_reader :amount, :units + def initialize(amount, units) + @amount = amount + @units = units + end + end + + it "raises a given instance of an exception with arguments other than the standard 'message'" do + @double.should_receive(:something).and_raise(OutOfGas.new(2, :oz)) + + begin + @double.something + fail "OutOfGas was not raised" + rescue OutOfGas => e + expect(e.amount).to eq 2 + expect(e.units).to eq :oz + end + end + + it "does not raise when told to if args dont match" do + @double.should_receive(:something).with(2).and_raise(RuntimeError) + expect { + @double.something 1 + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "throws when told to" do + @double.should_receive(:something).and_throw(:blech) + expect { + @double.something + }.to throw_symbol(:blech) + end + + it "ignores args on any args" do + @double.should_receive(:something).at_least(:once).with(any_args) + @double.something + @double.something 1 + @double.something "a", 2 + @double.something [], {}, "joe", 7 + verify @double + end + + it "fails on no args if any args received" do + @double.should_receive(:something).with(no_args()) + expect { + @double.something 1 + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (no args)\n got: (1)") + end + + it "fails when args are expected but none are received" do + @double.should_receive(:something).with(1) + expect { + @double.something + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (1)\n got: (no args)") + end + + it "returns value from block by default" do + @double.stub(:method_that_yields).and_yield + value = @double.method_that_yields { :returned_obj } + expect(value).to eq :returned_obj + verify @double + end + + it "yields 0 args to blocks that take a variable number of arguments" do + @double.should_receive(:yield_back).with(no_args()).once.and_yield + a = nil + @double.yield_back {|*x| a = x} + expect(a).to eq [] + verify @double + end + + it "yields 0 args multiple times to blocks that take a variable number of arguments" do + @double.should_receive(:yield_back).once.with(no_args()).once.and_yield. + and_yield + b = [] + @double.yield_back {|*a| b << a} + expect(b).to eq [ [], [] ] + verify @double + end - it 'allows setter methods to be stubbed' do - dbl = double('foo=' => 1) + it "yields one arg to blocks that take a variable number of arguments" do + @double.should_receive(:yield_back).with(no_args()).once.and_yield(99) + a = nil + @double.yield_back {|*x| a = x} + expect(a).to eq [99] + verify @double + end - # Note the specified return value is thrown away. This is a Ruby semantics - # thing. You cannot change the return value of assignment. - expect(dbl.foo = "bar").to eq("bar") + it "yields one arg 3 times consecutively to blocks that take a variable number of arguments" do + @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99). + and_yield(43). + and_yield("something fruity") + b = [] + @double.yield_back {|*a| b << a} + expect(b).to eq [[99], [43], ["something fruity"]] + verify @double + end + + it "yields many args to blocks that take a variable number of arguments" do + @double.should_receive(:yield_back).with(no_args()).once.and_yield(99, 27, "go") + a = nil + @double.yield_back {|*x| a = x} + expect(a).to eq [99, 27, "go"] + verify @double + end + + it "yields many args 3 times consecutively to blocks that take a variable number of arguments" do + @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99, :green, "go"). + and_yield("wait", :amber). + and_yield("stop", 12, :red) + b = [] + @double.yield_back {|*a| b << a} + expect(b).to eq [[99, :green, "go"], ["wait", :amber], ["stop", 12, :red]] + verify @double + end + + it "yields single value" do + @double.should_receive(:yield_back).with(no_args()).once.and_yield(99) + a = nil + @double.yield_back {|x| a = x} + expect(a).to eq 99 + verify @double + end + + it "yields single value 3 times consecutively" do + @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99). + and_yield(43). + and_yield("something fruity") + b = [] + @double.yield_back {|a| b << a} + expect(b).to eq [99, 43, "something fruity"] + verify @double + end + + it "yields two values" do + @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + a, b = nil + @double.yield_back {|x,y| a=x; b=y} + expect(a).to eq 'wha' + expect(b).to eq 'zup' + verify @double + end + + it "yields two values 3 times consecutively" do + @double.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). + and_yield('not', 'down'). + and_yield(14, 65) + c = [] + @double.yield_back {|a,b| c << [a, b]} + expect(c).to eq [['wha', 'zup'], ['not', 'down'], [14, 65]] + verify @double + end + + it "fails when calling yielding method with wrong arity" do + @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + expect { + @double.yield_back {|a|} + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" yielded |\"wha\", \"zup\"| to block with arity of 1") + end + + it "fails when calling yielding method consecutively with wrong arity" do + @double.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). + and_yield('down'). + and_yield(14, 65) + expect { + c = [] + @double.yield_back {|a,b| c << [a, b]} + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" yielded |\"down\"| to block with arity of 2") + end + + it "fails when calling yielding method without block" do + @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + expect { + @double.yield_back + }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" asked to yield |[\"wha\", \"zup\"]| but no block was passed") + end + + it "is able to double send" do + @double.should_receive(:send).with(any_args) + @double.send 'hi' + verify @double + end + + it "is able to raise from method calling yielding double" do + @double.should_receive(:yield_me).and_yield 44 + + expect { + @double.yield_me do |x| + raise "Bang" + end + }.to raise_error(StandardError, "Bang") + + verify @double + end + + it "clears expectations after verify" do + @double.should_receive(:foobar) + @double.foobar + verify @double + expect { + @double.foobar + }.to raise_error(RSpec::Mocks::MockExpectationError, %q|Double "test double" received unexpected message :foobar with (no args)|) + end + + it "restores objects to their original state on rspec_reset" do + double = double("this is a double") + double.should_receive(:blah) + reset double + verify double #should throw if reset didn't work + end + + it "works 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 Double#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 + + + @double.should_receive(:foobar) + @double.foobar + verify @double + + expect { @double.foobar }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "temporarily replaces a method stub on a double" do + @double.stub(:msg).and_return(:stub_value) + @double.should_receive(:msg).with(:arg).and_return(:double_value) + expect(@double.msg(:arg)).to equal(:double_value) + expect(@double.msg).to equal(:stub_value) + expect(@double.msg).to equal(:stub_value) + verify @double + end + + it "does not require a different signature to replace a method stub" do + @double.stub(:msg).and_return(:stub_value) + @double.should_receive(:msg).and_return(:double_value) + expect(@double.msg(:arg)).to equal(:double_value) + expect(@double.msg).to equal(:stub_value) + expect(@double.msg).to equal(:stub_value) + verify @double + end + + it "raises an error when a previously stubbed method has a negative expectation" do + @double.stub(:msg).and_return(:stub_value) + @double.should_not_receive(:msg) + expect { @double.msg(:arg) }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "temporarily replaces a method stub on a non-double" do + non_double = Object.new + non_double.stub(:msg).and_return(:stub_value) + non_double.should_receive(:msg).with(:arg).and_return(:double_value) + expect(non_double.msg(:arg)).to equal(:double_value) + expect(non_double.msg).to equal(:stub_value) + expect(non_double.msg).to equal(:stub_value) + verify non_double + end + + it "returns the stubbed value when no new value specified" do + @double.stub(:msg).and_return(:stub_value) + @double.should_receive(:msg) + expect(@double.msg).to equal(:stub_value) + verify @double + end + + it "returns the stubbed value when stubbed with args and no new value specified" do + @double.stub(:msg).with(:arg).and_return(:stub_value) + @double.should_receive(:msg).with(:arg) + expect(@double.msg(:arg)).to equal(:stub_value) + verify @double + end + + it "does not mess with the stub's yielded values when also doubleed" do + @double.stub(:yield_back).and_yield(:stub_value) + @double.should_receive(:yield_back).and_yield(:double_value) + @double.yield_back{|v| expect(v).to eq :double_value } + @double.yield_back{|v| expect(v).to eq :stub_value } + verify @double + end + + it "can yield multiple times when told to do so" do + @double.stub(:foo).and_yield(:stub_value) + @double.should_receive(:foo).and_yield(:first_yield).and_yield(:second_yield) + + expect { |b| @double.foo(&b) }.to yield_successive_args(:first_yield, :second_yield) + expect { |b| @double.foo(&b) }.to yield_with_args(:stub_value) + + verify @double + end + + it "assigns stub return values" do + double = RSpec::Mocks::Double.new('name', :message => :response) + expect(double.message).to eq :response + end + + end + + describe "a double message receiving a block" do + before(:each) do + @double = double("double") + @calls = 0 + end + + def add_call + @calls = @calls + 1 + end + + it "calls the block after #should_receive" do + @double.should_receive(:foo) { add_call } + + @double.foo + + expect(@calls).to eq 1 + end + + it "calls the block after #should_receive after a similar stub" do + @double.stub(:foo).and_return(:bar) + @double.should_receive(:foo) { add_call } + + @double.foo + + expect(@calls).to eq 1 + end + + it "calls the block after #once" do + @double.should_receive(:foo).once { add_call } + + @double.foo + + expect(@calls).to eq 1 + end + + it "calls the block after #twice" do + @double.should_receive(:foo).twice { add_call } + + @double.foo + @double.foo + + expect(@calls).to eq 2 + end + + it "calls the block after #times" do + @double.should_receive(:foo).exactly(10).times { add_call } + + (1..10).each { @double.foo } + + expect(@calls).to eq 10 + end + + it "calls the block after #ordered" do + @double.should_receive(:foo).ordered { add_call } + @double.should_receive(:bar).ordered { add_call } + + @double.foo + @double.bar + + expect(@calls).to eq 2 + end + end + + describe 'string representation generated by #to_s' do + it 'does not contain < because that might lead to invalid HTML in some situations' do + double = double("Dog") + valid_html_str = "#{double}" + expect(valid_html_str).not_to include('<') + end + end + + describe "string representation generated by #to_str" do + it "looks the same as #to_s" do + double = double("Foo") + expect(double.to_str).to eq double.to_s + end + end + + describe "double created with no name" do + it "does not use a name in a failure message" do + double = double() + expect {double.foo}.to raise_error(/Double received/) + end + + it "does respond to initially stubbed methods" do + double = double(:foo => "woo", :bar => "car") + expect(double.foo).to eq "woo" + expect(double.bar).to eq "car" + end + end + + describe "==" do + it "sends '== self' to the comparison object" do + first = double('first') + second = double('second') + + first.should_receive(:==).with(second) + second == first + end + end + + describe "with" do + before { @double = double('double') } + context "with args" do + context "with matching args" do + it "passes" do + @double.should_receive(:foo).with('bar') + @double.foo('bar') + end + end + + context "with non-matching args" do + it "fails" do + @double.should_receive(:foo).with('bar') + expect do + @double.foo('baz') + end.to raise_error + reset @double + end + end + + context "with non-matching doubles" do + it "fails" do + d1 = double('1') + d2 = double('2') + @double.should_receive(:foo).with(d1) + expect do + @double.foo(d2) + end.to raise_error + reset @double + end + end + + context "with non-matching doubles as_null_object" do + it "fails" do + d1 = double('1').as_null_object + d2 = double('2').as_null_object + @double.should_receive(:foo).with(d1) + expect do + @double.foo(d2) + end.to raise_error + reset @double + end + end + end + + context "with a block" do + context "with matching args" do + it "returns the result of the block" do + @double.should_receive(:foo).with('bar') { 'baz' } + expect(@double.foo('bar')).to eq('baz') + end + end + + context "with non-matching args" do + it "fails" do + @double.should_receive(:foo).with('bar') { 'baz' } + expect do + expect(@double.foo('wrong')).to eq('baz') + end.to raise_error(/received :foo with unexpected arguments/) + reset @double + end + end + end + end end end + diff --git a/spec/rspec/mocks/mock_spec.rb b/spec/rspec/mocks/mock_spec.rb deleted file mode 100644 index 4b70880b4..000000000 --- a/spec/rspec/mocks/mock_spec.rb +++ /dev/null @@ -1,788 +0,0 @@ -require 'spec_helper' - -module RSpec - module Mocks - describe Mock do - before(:each) { @double = double("test double") } - after(:each) { reset @double } - - it "has method_missing as private" do - expect(RSpec::Mocks::Mock.private_instance_methods).to include_method(:method_missing) - end - - it "does not respond_to? method_missing (because it's private)" do - expect(RSpec::Mocks::Mock.new).not_to respond_to(:method_missing) - end - - it "reports line number of expectation of unreceived message" do - expected_error_line = __LINE__; @double.should_receive(:wont_happen).with("x", 3) - begin - verify @double - violated - rescue RSpec::Mocks::MockExpectationError => e - # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line - expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) - end - end - - it "reports line number of expectation of unreceived message after #should_receive after similar stub" do - @double.stub(:wont_happen) - expected_error_line = __LINE__; @double.should_receive(:wont_happen).with("x", 3) - begin - verify @double - violated - rescue RSpec::Mocks::MockExpectationError => e - # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line - expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) - end - end - - it "passes when not receiving message specified as not to be received" do - @double.should_not_receive(:not_expected) - verify @double - end - - it "prevents confusing double-negative expressions involving `never`" do - expect { - @double.should_not_receive(:not_expected).never - }.to raise_error(/trying to negate it again/) - end - - it "warns when `and_return` is called on a negative expectation" do - expect { - @double.should_not_receive(:do_something).and_return(1) - }.to raise_error(/not supported/) - - expect { - expect(@double).not_to receive(:do_something).and_return(1) - }.to raise_error(/not supported/) - - expect { - expect(@double).to receive(:do_something).never.and_return(1) - }.to raise_error(/not supported/) - end - - it "passes when receiving message specified as not to be received with different args" do - @double.should_not_receive(:message).with("unwanted text") - @double.should_receive(:message).with("other text") - @double.message "other text" - verify @double - end - - it "fails when receiving message specified as not to be received" do - @double.should_not_receive(:not_expected) - expect { - @double.not_expected - violated - }.to raise_error( - RSpec::Mocks::MockExpectationError, - %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| - ) - end - - it "fails when receiving message specified as not to be received with args" do - @double.should_not_receive(:not_expected).with("unexpected text") - expect { - @double.not_expected("unexpected text") - violated - }.to raise_error( - RSpec::Mocks::MockExpectationError, - %Q|(Double "test double").not_expected("unexpected text")\n expected: 0 times with arguments: ("unexpected text")\n received: 1 time with arguments: ("unexpected text")| - ) - end - - it "fails when array arguments do not match" do - @double.should_not_receive(:not_expected).with(["do not want"]) - expect { - @double.not_expected(["do not want"]) - violated - }.to raise_error( - RSpec::Mocks::MockExpectationError, - %Q|(Double "test double").not_expected(["do not want"])\n expected: 0 times with arguments: (["do not want"])\n received: 1 time with arguments: (["do not want"])| - ) - end - - it "passes when receiving message specified as not to be received with wrong args" do - @double.should_not_receive(:not_expected).with("unexpected text") - @double.not_expected "really unexpected text" - verify @double - - @double.should_receive(:not_expected).with("unexpected text").never - @double.not_expected "really unexpected text" - verify @double - end - - it 'does not get confused when `should_not_received` is used with a string and symbol message' do - @double.stub(:foo) { 3 } - @double.should_not_receive(:foo).with(1) - @double.should_not_receive("foo").with(2) - - expect(@double.foo).to eq(3) - verify @double - end - - it 'does not get confused when `should_received` is used with a string and symbol message' do - @double.should_receive(:foo).with(1) - @double.should_receive("foo").with(2) - - @double.foo(1) - @double.foo(2) - - verify @double - end - - it "allows block to calculate return values" do - @double.should_receive(:something).with("a","b","c").and_return { |a,b,c| c+b+a } - expect(@double.something("a","b","c")).to eq "cba" - verify @double - end - - it "allows parameter as return value" do - @double.should_receive(:something).with("a","b","c").and_return("booh") - expect(@double.something("a","b","c")).to eq "booh" - verify @double - end - - it "returns the previously stubbed value if no return value is set" do - @double.stub(:something).with("a","b","c").and_return(:stubbed_value) - @double.should_receive(:something).with("a","b","c") - expect(@double.something("a","b","c")).to eq :stubbed_value - verify @double - end - - it "returns nil if no return value is set and there is no previously stubbed value" do - @double.should_receive(:something).with("a","b","c") - expect(@double.something("a","b","c")).to be_nil - verify @double - end - - it "raises exception if args don't match when method called" do - @double.should_receive(:something).with("a","b","c").and_return("booh") - expect { - @double.something("a","d","c") - violated - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") - end - - describe "even when a similar expectation with different arguments exist" do - it "raises exception if args don't match when method called, correctly reporting the offending arguments" do - @double.should_receive(:something).with("a","b","c").once - @double.should_receive(:something).with("z","x","c").once - expect { - @double.something("a","b","c") - @double.something("z","x","g") - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"z\", \"x\", \"c\")\n got: (\"z\", \"x\", \"g\")") - end - end - - it "raises exception if args don't match when method called even when the method is stubbed" do - @double.stub(:something) - @double.should_receive(:something).with("a","b","c") - expect { - @double.something("a","d","c") - verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") - end - - it "raises exception if args don't match when method called even when using null_object" do - @double = double("test double").as_null_object - @double.should_receive(:something).with("a","b","c") - expect { - @double.something("a","d","c") - verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") - end - - describe 'with a method that has a default argument' do - it "raises an exception if the arguments don't match when the method is called, correctly reporting the offending arguments" do - def @double.method_with_default_argument(arg={}); end - @double.should_receive(:method_with_default_argument).with({}) - - expect { - @double.method_with_default_argument(nil) - verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)") - end - end - - it "fails if unexpected method called" do - expect { - @double.something("a","b","c") - violated - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received unexpected message :something with (\"a\", \"b\", \"c\")") - end - - it "uses block for expectation if provided" do - @double.should_receive(:something) do | a, b | - expect(a).to eq "a" - expect(b).to eq "b" - "booh" - end - expect(@double.something("a", "b")).to eq "booh" - verify @double - end - - it "fails if expectation block fails" do - @double.should_receive(:something) do |bool| - expect(bool).to be_truthy - end - - expect { - @double.something false - }.to raise_error(RSpec::Expectations::ExpectationNotMetError) - end - - it "is wrappable in an array" do - expect( Array(@double) ).to eq([@double]) - end - - it "is wrappable in an array when a null object" do - expect( Array(@double.as_null_object) ).to eq [@double] - end - - it "responds to to_ary as a null object" do - expect(@double.as_null_object.to_ary).to eq nil - end - - it "responds to to_a as a null object" do - if RUBY_VERSION.to_f > 1.8 - expect(@double.as_null_object.to_a).to eq nil - else - expect(@double.as_null_object.to_a).to eq [@double] - end - end - - it "passes proc to expectation block without an argument" do - @double.should_receive(:foo) { |&block| expect(block.call).to eq(:bar) } - @double.foo { :bar } - end - - it "passes proc to expectation block with an argument" do - @double.should_receive(:foo) { |arg, &block| expect(block.call).to eq(:bar) } - @double.foo(:arg) { :bar } - end - - it "passes proc to stub block without an argurment" do - @double.stub(:foo) { |&block| expect(block.call).to eq(:bar) } - @double.foo { :bar } - end - - it "passes proc to stub block with an argument" do - @double.stub(:foo) { |arg, &block| expect(block.call).to eq(:bar) } - @double.foo(:arg) { :bar } - end - - it "fails right away when method defined as never is received" do - @double.should_receive(:not_expected).never - expect { @double.not_expected }. - to raise_error(RSpec::Mocks::MockExpectationError, - %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| - ) - end - - it "raises RuntimeError by default" do - @double.should_receive(:something).and_raise - expect { @double.something }.to raise_error(RuntimeError) - end - - it "raises RuntimeError with a message by default" do - @double.should_receive(:something).and_raise("error message") - expect { @double.something }.to raise_error(RuntimeError, "error message") - end - - it "raises an exception of a given type without an error message" do - @double.should_receive(:something).and_raise(StandardError) - expect { @double.something }.to raise_error(StandardError) - end - - it "raises an exception of a given type with a message" do - @double.should_receive(:something).and_raise(RuntimeError, "error message") - expect { @double.something }.to raise_error(RuntimeError, "error message") - end - - it "raises a given instance of an exception" do - @double.should_receive(:something).and_raise(RuntimeError.new("error message")) - expect { @double.something }.to raise_error(RuntimeError, "error message") - end - - class OutOfGas < StandardError - attr_reader :amount, :units - def initialize(amount, units) - @amount = amount - @units = units - end - end - - it "raises a given instance of an exception with arguments other than the standard 'message'" do - @double.should_receive(:something).and_raise(OutOfGas.new(2, :oz)) - - begin - @double.something - fail "OutOfGas was not raised" - rescue OutOfGas => e - expect(e.amount).to eq 2 - expect(e.units).to eq :oz - end - end - - it "does not raise when told to if args dont match" do - @double.should_receive(:something).with(2).and_raise(RuntimeError) - expect { - @double.something 1 - }.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "throws when told to" do - @double.should_receive(:something).and_throw(:blech) - expect { - @double.something - }.to throw_symbol(:blech) - end - - it "ignores args on any args" do - @double.should_receive(:something).at_least(:once).with(any_args) - @double.something - @double.something 1 - @double.something "a", 2 - @double.something [], {}, "joe", 7 - verify @double - end - - it "fails on no args if any args received" do - @double.should_receive(:something).with(no_args()) - expect { - @double.something 1 - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (no args)\n got: (1)") - end - - it "fails when args are expected but none are received" do - @double.should_receive(:something).with(1) - expect { - @double.something - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (1)\n got: (no args)") - end - - it "returns value from block by default" do - @double.stub(:method_that_yields).and_yield - value = @double.method_that_yields { :returned_obj } - expect(value).to eq :returned_obj - verify @double - end - - it "yields 0 args to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield - a = nil - @double.yield_back {|*x| a = x} - expect(a).to eq [] - verify @double - end - - it "yields 0 args multiple times to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield. - and_yield - b = [] - @double.yield_back {|*a| b << a} - expect(b).to eq [ [], [] ] - verify @double - end - - it "yields one arg to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield(99) - a = nil - @double.yield_back {|*x| a = x} - expect(a).to eq [99] - verify @double - end - - it "yields one arg 3 times consecutively to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99). - and_yield(43). - and_yield("something fruity") - b = [] - @double.yield_back {|*a| b << a} - expect(b).to eq [[99], [43], ["something fruity"]] - verify @double - end - - it "yields many args to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield(99, 27, "go") - a = nil - @double.yield_back {|*x| a = x} - expect(a).to eq [99, 27, "go"] - verify @double - end - - it "yields many args 3 times consecutively to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99, :green, "go"). - and_yield("wait", :amber). - and_yield("stop", 12, :red) - b = [] - @double.yield_back {|*a| b << a} - expect(b).to eq [[99, :green, "go"], ["wait", :amber], ["stop", 12, :red]] - verify @double - end - - it "yields single value" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield(99) - a = nil - @double.yield_back {|x| a = x} - expect(a).to eq 99 - verify @double - end - - it "yields single value 3 times consecutively" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99). - and_yield(43). - and_yield("something fruity") - b = [] - @double.yield_back {|a| b << a} - expect(b).to eq [99, 43, "something fruity"] - verify @double - end - - it "yields two values" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') - a, b = nil - @double.yield_back {|x,y| a=x; b=y} - expect(a).to eq 'wha' - expect(b).to eq 'zup' - verify @double - end - - it "yields two values 3 times consecutively" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). - and_yield('not', 'down'). - and_yield(14, 65) - c = [] - @double.yield_back {|a,b| c << [a, b]} - expect(c).to eq [['wha', 'zup'], ['not', 'down'], [14, 65]] - verify @double - end - - it "fails when calling yielding method with wrong arity" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') - expect { - @double.yield_back {|a|} - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" yielded |\"wha\", \"zup\"| to block with arity of 1") - end - - it "fails when calling yielding method consecutively with wrong arity" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). - and_yield('down'). - and_yield(14, 65) - expect { - c = [] - @double.yield_back {|a,b| c << [a, b]} - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" yielded |\"down\"| to block with arity of 2") - end - - it "fails when calling yielding method without block" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') - expect { - @double.yield_back - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" asked to yield |[\"wha\", \"zup\"]| but no block was passed") - end - - it "is able to double send" do - @double.should_receive(:send).with(any_args) - @double.send 'hi' - verify @double - end - - it "is able to raise from method calling yielding double" do - @double.should_receive(:yield_me).and_yield 44 - - expect { - @double.yield_me do |x| - raise "Bang" - end - }.to raise_error(StandardError, "Bang") - - verify @double - end - - it "clears expectations after verify" do - @double.should_receive(:foobar) - @double.foobar - verify @double - expect { - @double.foobar - }.to raise_error(RSpec::Mocks::MockExpectationError, %q|Double "test double" received unexpected message :foobar with (no args)|) - end - - it "restores objects to their original state on rspec_reset" do - double = double("this is a double") - double.should_receive(:blah) - reset double - verify double #should throw if reset didn't work - end - - it "works 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 - - - @double.should_receive(:foobar) - @double.foobar - verify @double - - expect { @double.foobar }.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "temporarily replaces a method stub on a double" do - @double.stub(:msg).and_return(:stub_value) - @double.should_receive(:msg).with(:arg).and_return(:double_value) - expect(@double.msg(:arg)).to equal(:double_value) - expect(@double.msg).to equal(:stub_value) - expect(@double.msg).to equal(:stub_value) - verify @double - end - - it "does not require a different signature to replace a method stub" do - @double.stub(:msg).and_return(:stub_value) - @double.should_receive(:msg).and_return(:double_value) - expect(@double.msg(:arg)).to equal(:double_value) - expect(@double.msg).to equal(:stub_value) - expect(@double.msg).to equal(:stub_value) - verify @double - end - - it "raises an error when a previously stubbed method has a negative expectation" do - @double.stub(:msg).and_return(:stub_value) - @double.should_not_receive(:msg) - expect { @double.msg(:arg) }.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "temporarily replaces a method stub on a non-double" do - non_double = Object.new - non_double.stub(:msg).and_return(:stub_value) - non_double.should_receive(:msg).with(:arg).and_return(:double_value) - expect(non_double.msg(:arg)).to equal(:double_value) - expect(non_double.msg).to equal(:stub_value) - expect(non_double.msg).to equal(:stub_value) - verify non_double - end - - it "returns the stubbed value when no new value specified" do - @double.stub(:msg).and_return(:stub_value) - @double.should_receive(:msg) - expect(@double.msg).to equal(:stub_value) - verify @double - end - - it "returns the stubbed value when stubbed with args and no new value specified" do - @double.stub(:msg).with(:arg).and_return(:stub_value) - @double.should_receive(:msg).with(:arg) - expect(@double.msg(:arg)).to equal(:stub_value) - verify @double - end - - it "does not mess with the stub's yielded values when also doubleed" do - @double.stub(:yield_back).and_yield(:stub_value) - @double.should_receive(:yield_back).and_yield(:double_value) - @double.yield_back{|v| expect(v).to eq :double_value } - @double.yield_back{|v| expect(v).to eq :stub_value } - verify @double - end - - it "can yield multiple times when told to do so" do - @double.stub(:foo).and_yield(:stub_value) - @double.should_receive(:foo).and_yield(:first_yield).and_yield(:second_yield) - - expect { |b| @double.foo(&b) }.to yield_successive_args(:first_yield, :second_yield) - expect { |b| @double.foo(&b) }.to yield_with_args(:stub_value) - - verify @double - end - - it "assigns stub return values" do - double = RSpec::Mocks::Mock.new('name', :message => :response) - expect(double.message).to eq :response - end - - end - - describe "a double message receiving a block" do - before(:each) do - @double = double("double") - @calls = 0 - end - - def add_call - @calls = @calls + 1 - end - - it "calls the block after #should_receive" do - @double.should_receive(:foo) { add_call } - - @double.foo - - expect(@calls).to eq 1 - end - - it "calls the block after #should_receive after a similar stub" do - @double.stub(:foo).and_return(:bar) - @double.should_receive(:foo) { add_call } - - @double.foo - - expect(@calls).to eq 1 - end - - it "calls the block after #once" do - @double.should_receive(:foo).once { add_call } - - @double.foo - - expect(@calls).to eq 1 - end - - it "calls the block after #twice" do - @double.should_receive(:foo).twice { add_call } - - @double.foo - @double.foo - - expect(@calls).to eq 2 - end - - it "calls the block after #times" do - @double.should_receive(:foo).exactly(10).times { add_call } - - (1..10).each { @double.foo } - - expect(@calls).to eq 10 - end - - it "calls the block after #ordered" do - @double.should_receive(:foo).ordered { add_call } - @double.should_receive(:bar).ordered { add_call } - - @double.foo - @double.bar - - expect(@calls).to eq 2 - end - end - - describe 'string representation generated by #to_s' do - it 'does not contain < because that might lead to invalid HTML in some situations' do - double = double("Dog") - valid_html_str = "#{double}" - expect(valid_html_str).not_to include('<') - end - end - - describe "string representation generated by #to_str" do - it "looks the same as #to_s" do - double = double("Foo") - expect(double.to_str).to eq double.to_s - end - end - - describe "double created with no name" do - it "does not use a name in a failure message" do - double = double() - expect {double.foo}.to raise_error(/Double received/) - end - - it "does respond to initially stubbed methods" do - double = double(:foo => "woo", :bar => "car") - expect(double.foo).to eq "woo" - expect(double.bar).to eq "car" - end - end - - describe "==" do - it "sends '== self' to the comparison object" do - first = double('first') - second = double('second') - - first.should_receive(:==).with(second) - second == first - end - end - - describe "with" do - before { @double = double('double') } - context "with args" do - context "with matching args" do - it "passes" do - @double.should_receive(:foo).with('bar') - @double.foo('bar') - end - end - - context "with non-matching args" do - it "fails" do - @double.should_receive(:foo).with('bar') - expect do - @double.foo('baz') - end.to raise_error - reset @double - end - end - - context "with non-matching doubles" do - it "fails" do - d1 = double('1') - d2 = double('2') - @double.should_receive(:foo).with(d1) - expect do - @double.foo(d2) - end.to raise_error - reset @double - end - end - - context "with non-matching doubles as_null_object" do - it "fails" do - d1 = double('1').as_null_object - d2 = double('2').as_null_object - @double.should_receive(:foo).with(d1) - expect do - @double.foo(d2) - end.to raise_error - reset @double - end - end - end - - context "with a block" do - context "with matching args" do - it "returns the result of the block" do - @double.should_receive(:foo).with('bar') { 'baz' } - expect(@double.foo('bar')).to eq('baz') - end - end - - context "with non-matching args" do - it "fails" do - @double.should_receive(:foo).with('bar') { 'baz' } - expect do - expect(@double.foo('wrong')).to eq('baz') - end.to raise_error(/received :foo with unexpected arguments/) - reset @double - end - end - end - end - end -end - diff --git a/spec/rspec/mocks/multiple_return_value_spec.rb b/spec/rspec/mocks/multiple_return_value_spec.rb index 9c15fedad..236c7e5de 100644 --- a/spec/rspec/mocks/multiple_return_value_spec.rb +++ b/spec/rspec/mocks/multiple_return_value_spec.rb @@ -56,7 +56,7 @@ module Mocks before(:each) do @double = double @return_values = [1,2,3] - @double.should_receive(:do_something).exactly(3).times.and_return(@return_values[0],@return_values[1],@return_values[2]) + @double.should_receive(:do_something).exactly(3).times.and_return(@return_values[0], @return_values[1], @return_values[2]) end it "returns values in order to consecutive calls" do @@ -104,7 +104,7 @@ module Mocks describe "a message expectation with multiple return values with a specified count larger than the number of values" do before(:each) do - @double = RSpec::Mocks::Mock.new("double") + @double = RSpec::Mocks::Double.new("double") @double.should_receive(:do_something).exactly(3).times.and_return(11, 22) end diff --git a/spec/rspec/mocks/options_hash_spec.rb b/spec/rspec/mocks/options_hash_spec.rb index e15051ae9..25785d38e 100644 --- a/spec/rspec/mocks/options_hash_spec.rb +++ b/spec/rspec/mocks/options_hash_spec.rb @@ -5,7 +5,7 @@ module Mocks describe "calling :should_receive with an options hash" do it "reports the file and line submitted with :expected_from" do begin - mock = RSpec::Mocks::Mock.new("a mock") + mock = RSpec::Mocks::Double.new("a mock") mock.should_receive(:message, :expected_from => "/path/to/blah.ext:37") verify mock rescue Exception => e @@ -16,7 +16,7 @@ module Mocks it "uses the message supplied with :message" do expect { - m = RSpec::Mocks::Mock.new("a mock") + m = RSpec::Mocks::Double.new("a mock") m.should_receive(:message, :message => "recebi nada") verify m }.to raise_error("recebi nada") @@ -24,7 +24,7 @@ module Mocks it "uses the message supplied with :message after a similar stub" do expect { - m = RSpec::Mocks::Mock.new("a mock") + m = RSpec::Mocks::Double.new("a mock") m.stub(:message) m.should_receive(:message, :message => "from mock") verify m diff --git a/spec/rspec/mocks/partial_mock_spec.rb b/spec/rspec/mocks/partial_double_spec.rb similarity index 99% rename from spec/rspec/mocks/partial_mock_spec.rb rename to spec/rspec/mocks/partial_double_spec.rb index 7fc0c8e7f..184c687ae 100644 --- a/spec/rspec/mocks/partial_mock_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -2,7 +2,7 @@ module RSpec module Mocks - describe "using a Partial Mock," do + describe "A partial double" do let(:object) { Object.new } it "names the class in the failure message" do diff --git a/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb similarity index 98% rename from spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb rename to spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb index 18da09152..5ce0398d5 100644 --- a/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +++ b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' module RSpec::Mocks - describe "PartialMockUsingMocksDirectly" do + describe "PartialDoubleUsingMocksDirectly" do let(:klass) do Class.new do module MethodMissing diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index 3058fa78d..9a59e7f0f 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -8,7 +8,7 @@ module RSpec::Mocks it 'returns proxies' do space.proxy_for("") - expect(space.proxies_of(String).map(&:class)).to eq([PartialMockProxy]) + expect(space.proxies_of(String).map(&:class)).to eq([PartialDoubleProxy]) end it 'returns only the proxies whose object is an instance of the given class' do diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 83e45e471..a0c015abb 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -2,7 +2,7 @@ describe RSpec::Mocks do describe "::setup" do - context "with an existing Mock::Space" do + context "with an existing Mocks::Space" do before do @orig_space = RSpec::Mocks::space end @@ -19,8 +19,8 @@ end end - context "with no pre-existing Mock::Space" do - it "initializes a Mock::Space" do + context "with no pre-existing Mocks::Space" do + it "initializes a Mocks::Space" do RSpec::Mocks::space = nil RSpec::Mocks::setup(Object.new) expect(RSpec::Mocks::space).not_to be_nil From 1eae4a80ec3220322c64b4b7c6e34477d46f5cf4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 10 Nov 2013 13:07:54 -0800 Subject: [PATCH 0309/1179] Add changelog. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 48146a476..1d88409e5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Breaking Changes for 3.0.0: * Blocks provided to `with` are always used as implementation. (Xavier Shay) * The config option (added in 2.99) to yield the receiver to `any_instance` implementation blocks now defaults to "on". (Sam Phippen) +* Rename `RSpec::Mocks::Mock` to `RSpec::Mocks::Double`. (Myron Marston) Enhancements: From 3a0d8a2cc8ecba1944d0cea80ff552d6f93215fc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 10 Nov 2013 13:11:48 -0800 Subject: [PATCH 0310/1179] Declare some more classes as not part of the public API. --- lib/rspec/mocks/proxy.rb | 1 + lib/rspec/mocks/verifying_double.rb | 5 +++++ lib/rspec/mocks/verifying_message_expecation.rb | 1 + lib/rspec/mocks/verifying_proxy.rb | 6 ++++-- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index b169735a7..232d5ff93 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -231,6 +231,7 @@ def find_almost_matching_stub(method_name, *args) end end + # @private class PartialDoubleProxy < Proxy def method_handle_for(message) if any_instance_class_recorder_observing_method?(@object.class, message) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 6087fe37b..a511656e6 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -3,6 +3,7 @@ module RSpec module Mocks + # @api private module VerifyingDouble def method_missing(message, *args, &block) # Null object conditional is an optimization. If not a null object, @@ -16,6 +17,7 @@ def method_missing(message, *args, &block) # A mock providing a custom proxy that can verify the validity of any # method stubs or expectations against the public instance methods of the # given class. + # @api private class InstanceVerifyingDouble include TestDouble include VerifyingDouble @@ -36,6 +38,7 @@ def __build_mock_proxy(order_group) # An awkward module necessary because we cannot otherwise have # ClassVerifyingDouble inherit from Module and still share these methods. + # @api private module ObjectVerifyingDoubleMethods include TestDouble include VerifyingDouble @@ -61,6 +64,7 @@ def as_stubbed_const(options = {}) # Similar to an InstanceVerifyingDouble, except that it verifies against # public methods of the given object. + # @api private class ObjectVerifyingDouble include ObjectVerifyingDoubleMethods end @@ -68,6 +72,7 @@ class ObjectVerifyingDouble # Effectively the same as an ObjectVerifyingDouble (since a class is a type # of object), except with Module in the inheritance chain so that # transferring nested constants to work. + # @api private class ClassVerifyingDouble < Module include ObjectVerifyingDoubleMethods end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index b98330701..7ca8ea2c3 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -6,6 +6,7 @@ module Mocks # A message expectation that knows about the real implementation of the # message being expected, so that it can verify that any expectations # have the correct arity. + # @api private class VerifyingMessageExpectation < MessageExpectation # A level of indirection is used here rather than just passing in the diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index cb148a926..af105c097 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -4,6 +4,7 @@ module RSpec module Mocks + # @api private module VerifyingProxyMethods def add_stub(location, method_name, opts={}, &implementation) ensure_implemented(method_name) @@ -70,6 +71,7 @@ def method_reference end end + # @api private class VerifyingPartialDoubleProxy < PartialDoubleProxy include VerifyingProxyMethods @@ -121,13 +123,13 @@ def ensure_arity!(arity) end end - # @api private - # # A VerifyingMethodDouble fetches the method to verify against from the # original object, using a MethodReference. This works for pure doubles, # but when the original object is itself the one being modified we need to # collapse the reference and the method double into a single object so that # we can access the original pristine method definition. + # + # @api private class VerifyingExistingMethodDouble < VerifyingMethodDouble def initialize(object, method_name, proxy) super(object, method_name, proxy, self) From 4cbc14a1b20abd7b0bd3908eec252effc360ed07 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 11 Nov 2013 07:30:50 -0800 Subject: [PATCH 0311/1179] Fixup changelog. [ci skip] --- Changelog.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 1d88409e5..3e65af32f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,15 @@ +### 3.0.0.beta2 Development +[full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...master) + +Breaking Changes for 3.0.0: + +* Rename `RSpec::Mocks::Mock` to `RSpec::Mocks::Double`. (Myron Marston) + +Bug Fixes: + +* Fix regression in 3.0.0.beta1 that caused `double("string_name" => :value)` + to stop working. (Xavier Shay) + ### 3.0.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) @@ -20,7 +32,6 @@ Breaking Changes for 3.0.0: * Blocks provided to `with` are always used as implementation. (Xavier Shay) * The config option (added in 2.99) to yield the receiver to `any_instance` implementation blocks now defaults to "on". (Sam Phippen) -* Rename `RSpec::Mocks::Mock` to `RSpec::Mocks::Double`. (Myron Marston) Enhancements: From cb027f60c19fffa539f07cf0c714ee7267979ace Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 11 Nov 2013 12:28:27 +1100 Subject: [PATCH 0312/1179] allow verification of private methods --- lib/rspec/mocks/method_reference.rb | 4 ++-- spec/rspec/mocks/verifying_double_spec.rb | 25 +++++++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index ce8c2b429..e6a6c9f54 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -49,7 +49,7 @@ def original_method class InstanceMethodReference < MethodReference private def method_implemented?(m) - m.method_defined?(@method_name) + m.method_defined?(@method_name) || m.private_method_defined?(@method_name) end # Ideally, we'd use `respond_to?` for `method_implemented?` but we need a @@ -80,7 +80,7 @@ def find_method(m) class ObjectMethodReference < MethodReference private def method_implemented?(m) - m.respond_to?(@method_name) + m.respond_to?(@method_name, true) end def method_defined?(m) diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 42ddc47b6..4d4e704f8 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -8,12 +8,12 @@ class LoadedClass def defined_instance_method; end def self.defined_class_method; end - def respond_to?(method_name) + def respond_to?(method_name, include_all = false) return true if method_name == :dynamic_instance_method super end - def self.respond_to?(method_name) + def self.respond_to?(method_name, include_all = false) return true if method_name == :dynamic_class_method super end @@ -23,6 +23,13 @@ def self.send end class Nested; end + +private + + def defined_private_method + "wink wink ;)" + end + end module RSpec @@ -79,6 +86,16 @@ module Mocks prevents { o.should_receive(:defined_class_method) } end + it 'allows instance methods that are private' do + o = instance_double('LoadedClass') + allow(o).to receive(:defined_private_method) + o.send :defined_private_method + + o2 = instance_double('LoadedClass') + expect(o2).to receive(:defined_private_method) + o2.send :defined_private_method + end + it 'does not allow dynamic methods to be expected' do # This isn't possible at the moment since an instance of the class # would be required for the verification, and we only have the @@ -266,6 +283,8 @@ module Mocks expect(o).to receive(:defined_instance_method) o.defined_instance_method + expect(o).to receive(:defined_private_method) + o.send :defined_private_method end it 'can create a double that matches the interface of any arbitrary object' do @@ -277,6 +296,8 @@ module Mocks expect(o).to receive(:defined_instance_method) o.defined_instance_method + expect(o).to receive(:defined_private_method) + o.send :defined_private_method end it 'does not allow transferring constants to an object' do From 568092c8dd44e65d1dfc5e97e5b96015e890998d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 12 Nov 2013 14:52:29 +1100 Subject: [PATCH 0313/1179] assert on verification of private class methods --- spec/rspec/mocks/verifying_double_spec.rb | 37 ++++++++++++++++------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 4d4e704f8..0b1af0692 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -5,21 +5,33 @@ class LoadedClass N = :n INSTANCE = LoadedClass.new - def defined_instance_method; end - def self.defined_class_method; end + class << self + + def respond_to?(method_name, include_all = false) + return true if method_name == :dynamic_class_method + super + end + + def defined_class_method + end + + def send + # fake out! + end + + private + + def defined_private_class_method + end - def respond_to?(method_name, include_all = false) - return true if method_name == :dynamic_instance_method - super end - def self.respond_to?(method_name, include_all = false) - return true if method_name == :dynamic_class_method - super + def defined_instance_method end - def self.send - # fake out! + def respond_to?(method_name, include_all = false) + return true if method_name == :dynamic_instance_method + super end class Nested; end @@ -172,8 +184,9 @@ module Mocks end it 'only allows class methods that exist to be stubbed' do - o = class_double('LoadedClass', :defined_class_method => 1) + o = class_double('LoadedClass', :defined_class_method => 1, :defined_private_class_method => 42) expect(o.defined_class_method).to eq(1) + expect(o.send :defined_private_class_method).to eq(42) prevents { o.stub(:undefined_instance_method) } prevents { o.stub(:defined_instance_method) } @@ -182,7 +195,9 @@ module Mocks it 'only allows class methods that exist to be expected' do o = class_double('LoadedClass') expect(o).to receive(:defined_class_method) + expect(o).to receive(:defined_private_class_method) o.defined_class_method + o.send :defined_private_class_method prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_instance_method) } From 32d85e96447df52ba41067d474a3ae0210756581 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 13 Nov 2013 18:26:29 -0800 Subject: [PATCH 0314/1179] Add verifying doubles to README. --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 56cf87720..1beaf7039 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,21 @@ expectations. book = double("book") ``` +Most of the time you will want some confidence that your doubles resemble an +existing object in your system. Verifying doubles are provided for this +purpose. If the existing object is available, they will prevent you from adding +stubs and expectations for methods that do not exist or that have an invalid +number of parameters. + +```ruby +book = instance_double("Book", :pages => 250) +``` + +Verifying doubles have some clever tricks to enable you to both test in +isolation without your dependencies loaded while still being able to validate +them against real objects. More detail is available in (their +documentation)[https://github.com/rspec/rspec-mocks/blob/master/features/verifying_doubles]. + ## Method Stubs A method stub is an implementation that returns a pre-determined value. Method From dd0485f1cb6d2422df6cfc25e048f7f601dd91b9 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 13 Nov 2013 18:27:47 -0800 Subject: [PATCH 0315/1179] Remove reference to mock and stub in features README. --- features/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/features/README.md b/features/README.md index a25f85c19..785cd21bc 100644 --- a/features/README.md +++ b/features/README.md @@ -19,10 +19,6 @@ during a code example. Use the `double` method to create one: double_account = double("Account") -You can also use the `mock` and `stub` methods to create test doubles, however -these methods are there for backward compatibility only and will likely be -deprecated and then removed from future versions. - ## Method Stubs A method stub is an instruction to an object (real or test double) to return a From c7ac60918d63d38ee94c41b663a3f148f834cf6f Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 13 Nov 2013 18:30:02 -0800 Subject: [PATCH 0316/1179] Consistent double documentation across README and features README. --- README.md | 7 +++---- features/README.md | 14 +++++++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1beaf7039..f68c47a02 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,11 @@ alike. ## Test Doubles -A Test Double is an object that stands in for a real object in a test. -RSpec creates test doubles that support method stubs and message -expectations. +A test double is an object that stands in for another object in your system +during a code example. Use the `double` method to create one: ```ruby -book = double("book") +account = double("Account") ``` Most of the time you will want some confidence that your doubles resemble an diff --git a/features/README.md b/features/README.md index 785cd21bc..62df93e89 100644 --- a/features/README.md +++ b/features/README.md @@ -17,7 +17,19 @@ invokes a _method_ with the same name as the message. A test double is an object that stands in for another object in your system during a code example. Use the `double` method to create one: - double_account = double("Account") + book = double("book") + +Most of the time you will want some confidence that your doubles resemble an +existing object in your system. Verifying doubles are provided for this +purpose. If the existing object is available, they will prevent you from adding +stubs and expectations for methods that do not exist or that have an invalid +number of parameters. + + book = instance_double("Book", :pages => 250) + +Verifying doubles have some clever tricks to enable you to both test in +isolation without your dependencies loaded while still being able to validate +them against real objects. ## Method Stubs From 4cae12c43e3c96c184dcb06fa8f70bd21fa23842 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 13 Nov 2013 19:24:38 -0800 Subject: [PATCH 0317/1179] Fix accidental reference to account. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f68c47a02..80249293b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A test double is an object that stands in for another object in your system during a code example. Use the `double` method to create one: ```ruby -account = double("Account") +book = double("book") ``` Most of the time you will want some confidence that your doubles resemble an From b01f908543d571bcde6c48ff02f332175dfb8ac2 Mon Sep 17 00:00:00 2001 From: Stuart Hicks Date: Fri, 15 Nov 2013 13:33:31 +0000 Subject: [PATCH 0318/1179] Update Readme to mention what the parameter is for double() and that it is optional --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80249293b..e6b343ba9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ alike. ## Test Doubles A test double is an object that stands in for another object in your system -during a code example. Use the `double` method to create one: +during a code example. Use the `double` method, passing in an optional identifier, to create one: ```ruby book = double("book") From 655294c4f776944012b618e84695f67afccea764 Mon Sep 17 00:00:00 2001 From: Michi Huber Date: Sat, 9 Mar 2013 13:30:43 +0100 Subject: [PATCH 0319/1179] Don't override let definitions with argument matchers see https://github.com/rspec/rspec-mocks/issues/102 ArgumentMatchers (together with ExampleMethods) were included into the ExampleGroup instance's metaclass. This caused argument matcher methods such as boolean() or hash_containing() to override let definitions with the same name. This commit fixes this bug by including ArgumentMatchers into the example group's class and not into the metaclass. Conflicts: Changelog.md lib/rspec/mocks.rb spec/rspec/mocks/mock_spec.rb (This commit originated in rspec/rspec-mocks#238. At this point, the rest of the changes in that aren't valid or needed, and the broken behavior these specs documented has been fixed upstream in rspec-core by rspec/rspec-core#1188) --- spec/rspec/mocks_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index a0c015abb..684adfa40 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -56,4 +56,17 @@ end end + describe "ArgumentMatchers not overriding let definitions" do + WithMatchers = Class.new { include RSpec::Mocks::ArgumentMatchers } + methods = WithMatchers.new.methods - Object.new.methods + + methods.each do |method| + let(method) { :a_thing } + + it "doesn't override a let named #{ method }" do + expect(send(method)).to be(:a_thing) + end + end + end end + From 0f55ff55edcd48238ddad5e025f54c59cd16fbd3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 17 Nov 2013 21:03:41 -0800 Subject: [PATCH 0320/1179] Simplify spec a bit. We don't need to exhaustively test every argument matchers method; one will do. --- spec/rspec/mocks_spec.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 684adfa40..ac761816e 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -56,16 +56,15 @@ end end - describe "ArgumentMatchers not overriding let definitions" do - WithMatchers = Class.new { include RSpec::Mocks::ArgumentMatchers } - methods = WithMatchers.new.methods - Object.new.methods + context "when there is a `let` declaration that overrides an argument matcher" do + let(:boolean) { :from_let } - methods.each do |method| - let(method) { :a_thing } + before do + expect(RSpec::Mocks::ArgumentMatchers.method_defined?(:boolean)).to be true + end - it "doesn't override a let named #{ method }" do - expect(send(method)).to be(:a_thing) - end + it 'allows the `let` definition to win' do + expect(boolean).to eq(:from_let) end end end From e6bf83c1d94221164338abdacf277acfcddb9755 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 17 Nov 2013 21:05:18 -0800 Subject: [PATCH 0321/1179] Remove unnecessary module inclusion. As of rspec/rspec-core#1188, rspec-core now takes care of including the ExampleMethods module. This should be faster (including once, rather than once-per-example) and fixes #102. --- lib/rspec/mocks.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 96b814716..072c1c29f 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -8,9 +8,6 @@ class << self attr_accessor :space def setup(host) - (class << host; self; end).class_exec do - include RSpec::Mocks::ExampleMethods - end self.space ||= RSpec::Mocks::Space.new end From 1d36254cb54646934528675125e92b198d390ddb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 17 Nov 2013 21:07:19 -0800 Subject: [PATCH 0322/1179] Prefer `def self.` for defining class methods. Using the singleton method syntax is problematic as further down in the file it's not clear that you are in the singleton class context. `def self.` makes it very clear you are defining class methods. --- lib/rspec/mocks.rb | 142 ++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 072c1c29f..4b24d0669 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -6,87 +6,87 @@ module RSpec module Mocks class << self attr_accessor :space + end - def setup(host) - self.space ||= RSpec::Mocks::Space.new - end + def self.setup(host) + self.space ||= RSpec::Mocks::Space.new + end - def verify - space.verify_all - end + def self.verify + space.verify_all + end - def teardown - space.reset_all - end + def self.teardown + space.reset_all + end - def proxy_for(object) - space.proxy_for(object) - end + def self.proxy_for(object) + space.proxy_for(object) + end - def proxies_of(klass) - space.proxies_of(klass) - end + def self.proxies_of(klass) + space.proxies_of(klass) + end - def any_instance_recorder_for(klass) - space.any_instance_recorder_for(klass) - end + def self.any_instance_recorder_for(klass) + space.any_instance_recorder_for(klass) + end - # Adds an allowance (stub) on `subject` - # - # @param subject the subject to which the message will be added - # @param message a symbol, representing the message that will be - # added. - # @param opts a hash of options, :expected_from is used to set the - # original call site - # @param block an optional implementation for the allowance - # - # @example Defines the implementation of `foo` on `bar`, using the passed block - # x = 0 - # RSpec::Mocks.allow_message(bar, :foo) { x += 1 } - def allow_message(subject, message, opts={}, &block) - orig_caller = opts.fetch(:expected_from) { - CallerFilter.first_non_rspec_line - } - ::RSpec::Mocks.proxy_for(subject). - add_stub(orig_caller, message, opts, &block) - end + # Adds an allowance (stub) on `subject` + # + # @param subject the subject to which the message will be added + # @param message a symbol, representing the message that will be + # added. + # @param opts a hash of options, :expected_from is used to set the + # original call site + # @param block an optional implementation for the allowance + # + # @example Defines the implementation of `foo` on `bar`, using the passed block + # x = 0 + # RSpec::Mocks.allow_message(bar, :foo) { x += 1 } + def self.allow_message(subject, message, opts={}, &block) + orig_caller = opts.fetch(:expected_from) { + CallerFilter.first_non_rspec_line + } + ::RSpec::Mocks.proxy_for(subject). + add_stub(orig_caller, message, opts, &block) + end - # Sets a message expectation on `subject`. - # @param subject the subject on which the message will be expected - # @param message a symbol, representing the message that will be - # expected. - # @param opts a hash of options, :expected_from is used to set the - # original call site - # @param block an optional implementation for the expectation - # - # @example Expect the message `foo` to receive `bar`, then call it - # RSpec::Mocks.expect_message(bar, :foo) - # bar.foo - def expect_message(subject, message, opts={}, &block) - orig_caller = opts.fetch(:expected_from) { - CallerFilter.first_non_rspec_line - } - ::RSpec::Mocks.proxy_for(subject). - add_message_expectation(orig_caller, message, opts, &block) - end + # Sets a message expectation on `subject`. + # @param subject the subject on which the message will be expected + # @param message a symbol, representing the message that will be + # expected. + # @param opts a hash of options, :expected_from is used to set the + # original call site + # @param block an optional implementation for the expectation + # + # @example Expect the message `foo` to receive `bar`, then call it + # RSpec::Mocks.expect_message(bar, :foo) + # bar.foo + def self.expect_message(subject, message, opts={}, &block) + orig_caller = opts.fetch(:expected_from) { + CallerFilter.first_non_rspec_line + } + ::RSpec::Mocks.proxy_for(subject). + add_message_expectation(orig_caller, message, opts, &block) + end - # @api private - KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) + # @api private + KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) - # @api private - # Used internally to get a method handle for a particular object - # and method name. - # - # Includes handling for a few special cases: - # - # - Objects that redefine #method (e.g. an HTTPRequest struct) - # - BasicObject subclasses that mixin a Kernel dup (e.g. SimpleDelegator) - def method_handle_for(object, method_name) - if ::Kernel === object - KERNEL_METHOD_METHOD.bind(object).call(method_name) - else - object.method(method_name) - end + # @api private + # Used internally to get a method handle for a particular object + # and method name. + # + # Includes handling for a few special cases: + # + # - Objects that redefine #method (e.g. an HTTPRequest struct) + # - BasicObject subclasses that mixin a Kernel dup (e.g. SimpleDelegator) + def self.method_handle_for(object, method_name) + if ::Kernel === object + KERNEL_METHOD_METHOD.bind(object).call(method_name) + else + object.method(method_name) end end From bf9320d556e66b1f3d8c1424856b3d8b9533ee07 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 17 Nov 2013 21:10:11 -0800 Subject: [PATCH 0323/1179] No need to re-setup space in each example. Setting it up once at load time is sufficient. This should be faster (doing no per-example setup is faster than doing some per-example setup). --- lib/rspec/mocks.rb | 4 +++- spec/rspec/mocks_spec.rb | 27 --------------------------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 4b24d0669..d09120d30 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -8,8 +8,10 @@ class << self attr_accessor :space end + self.space = RSpec::Mocks::Space.new + def self.setup(host) - self.space ||= RSpec::Mocks::Space.new + # Nothing to do for now end def self.verify diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index ac761816e..2d5531941 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -1,33 +1,6 @@ require "spec_helper" describe RSpec::Mocks do - describe "::setup" do - context "with an existing Mocks::Space" do - before do - @orig_space = RSpec::Mocks::space - end - - after do - RSpec::Mocks::space = @orig_space - end - - it "memoizes the space" do - RSpec::Mocks::setup(Object.new) - space = RSpec::Mocks::space - RSpec::Mocks::setup(Object.new) - expect(RSpec::Mocks::space).to equal(space) - end - end - - context "with no pre-existing Mocks::Space" do - it "initializes a Mocks::Space" do - RSpec::Mocks::space = nil - RSpec::Mocks::setup(Object.new) - expect(RSpec::Mocks::space).not_to be_nil - end - end - end - describe "::verify" do it "delegates to the space" do foo = double From 4ab5bb3cc84c95b53dd26fb07f4451b3dc165767 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 17 Nov 2013 21:30:36 -0800 Subject: [PATCH 0324/1179] Document the methods defined in RSpec::Mocks. --- lib/rspec/mocks.rb | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index d09120d30..b6217e9ab 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -2,38 +2,38 @@ require 'rspec/mocks/version' module RSpec - + # Contains top-level utility methods. While this contains a few + # public methods, these are not generally meant to be called from + # a test or example. They exist primarily for integration with + # test frameworks (such as rspec-core). module Mocks class << self + # Stores rspec-mocks' global state. + # @api private attr_accessor :space end self.space = RSpec::Mocks::Space.new + # Performs per-test/example setup. This should be called before + # an test or example begins. def self.setup(host) # Nothing to do for now end + # Verifies any message expectations that were set during the + # test or example. This should be called at the end of an example. def self.verify space.verify_all end + # Cleans up all test double state (including any methods that were + # redefined on partial doubles). This _must_ be called after + # each example, even if an error was raised during the example. def self.teardown space.reset_all end - def self.proxy_for(object) - space.proxy_for(object) - end - - def self.proxies_of(klass) - space.proxies_of(klass) - end - - def self.any_instance_recorder_for(klass) - space.any_instance_recorder_for(klass) - end - # Adds an allowance (stub) on `subject` # # @param subject the subject to which the message will be added @@ -73,6 +73,24 @@ def self.expect_message(subject, message, opts={}, &block) add_message_expectation(orig_caller, message, opts, &block) end + # @api private + # Returns the mock proxy for the given object. + def self.proxy_for(object) + space.proxy_for(object) + end + + # @api private + # Returns the mock proxies for instances of the given class. + def self.proxies_of(klass) + space.proxies_of(klass) + end + + # @api private + # Returns the any instance recorder for the given class. + def self.any_instance_recorder_for(klass) + space.any_instance_recorder_for(klass) + end + # @api private KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) From 7af0bec419b2462be3f6ff01fbf05e06c365e985 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 17 Nov 2013 21:41:05 -0800 Subject: [PATCH 0325/1179] Update cukes to demo how to use rspec-mocks outside rspec-core. --- features/outside_rspec/configuration.feature | 60 -------------------- features/outside_rspec/standalone.feature | 2 +- features/test_frameworks/test_unit.feature | 29 ++++++---- lib/rspec/mocks/standalone.rb | 3 +- 4 files changed, 20 insertions(+), 74 deletions(-) delete mode 100644 features/outside_rspec/configuration.feature diff --git a/features/outside_rspec/configuration.feature b/features/outside_rspec/configuration.feature deleted file mode 100644 index b82f8578b..000000000 --- a/features/outside_rspec/configuration.feature +++ /dev/null @@ -1,60 +0,0 @@ -Feature: configure any test framework to use rspec-mocks - - Test frameworks that want to use rspec-mocks can use - RSpec::Mocks::setup(self) to hook into rspec-mocks. Doing so adds the - following: - - To the object passed to setup: - - double # creates a test double - mock # creates a test double - stub # creates a test double - - To every object in the system: - - should_receive - should_not_receive - stub - - NOTICE: the stub() method that is added to the object passed to setup is not - the same stub() method that is added to every other object. - - Scenario: RSpec::Mocks::setup(object) adds double, mock, and stub methods to the submitted object - Given a file named "foo.rb" with: - """ruby - require 'rspec/mocks' - - class CodeExample - def init - RSpec::Mocks::setup(self) - end - end - - example = CodeExample.new - example.init - - puts example.respond_to?(:double) - """ - - When I run `ruby foo.rb` - Then the output should contain "true" - But the output should not contain "false" - - Scenario: RSpec::Mocks::setup(anything) adds methods to Object - Given a file named "foo.rb" with: - """ruby - require 'rspec/mocks' - - RSpec::Mocks::setup(Object.new) - - obj = Object.new - - puts obj.respond_to?(:should_receive) - puts obj.respond_to?(:should_not_receive) - puts obj.respond_to?(:stub) - """ - - When I run `ruby foo.rb` - Then the output should contain "true" - But the output should not contain "false" - diff --git a/features/outside_rspec/standalone.feature b/features/outside_rspec/standalone.feature index c7daffb5a..44392ad00 100644 --- a/features/outside_rspec/standalone.feature +++ b/features/outside_rspec/standalone.feature @@ -10,7 +10,7 @@ Feature: standalone require "rspec/mocks/standalone" greeter = double("greeter") - greeter.stub(:say_hi) { "Hello!" } + allow(greeter).to receive(:say_hi) { "Hello!" } puts greeter.say_hi """ When I run `ruby example.rb` diff --git a/features/test_frameworks/test_unit.feature b/features/test_frameworks/test_unit.feature index 702d6aa00..366603053 100644 --- a/features/test_frameworks/test_unit.feature +++ b/features/test_frameworks/test_unit.feature @@ -13,31 +13,38 @@ Feature: Test::Unit integration require 'rspec/mocks' class RSpecMocksTest < Test::Unit::TestCase - def setup - RSpec::Mocks.setup(Object) - RSpec::Mocks.setup(self) + include RSpec::Mocks::ExampleMethods + + def teardown + RSpec::Mocks.verify + ensure + RSpec::Mocks.teardown end - def test_passing_expectation + def test_passing_positive_expectation obj = Object.new expect(obj).to receive(:message) obj.message end - def test_failing_expectation + def test_failing_positive_expectation obj = Object.new - expect(obj).to_not receive(:message) + expect(obj).to receive(:message) obj.message end - def test_with_deprecation_warning + def test_passing_negative_expectation obj = Object.new - obj.stub(:old_message) { RSpec.deprecate(:old_message, :replacement => :message) } - obj.old_message + expect(obj).to_not receive(:message) + end + + def test_failing_negative_expectation + obj = Object.new + expect(obj).to_not receive(:message) + obj.message end end """ When I run `ruby rspec_mocks_test.rb` - Then the output should contain "3 tests, 0 assertions, 0 failures, 1 errors" or "3 tests, 0 assertions, 1 failures, 0 errors" + Then the output should contain "4 tests, 0 assertions, 0 failures, 1 errors" or "4 tests, 0 assertions, 1 failures, 0 errors" And the output should contain "expected: 0 times with any arguments" - And the output should contain "old_message is deprecated" diff --git a/lib/rspec/mocks/standalone.rb b/lib/rspec/mocks/standalone.rb index 05d917475..f9330fbe7 100644 --- a/lib/rspec/mocks/standalone.rb +++ b/lib/rspec/mocks/standalone.rb @@ -1,3 +1,2 @@ require 'rspec/mocks' - -RSpec::Mocks.setup(self) +include RSpec::Mocks::ExampleMethods From b8d745e896324f96a479783b16a1af4cc815712f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 17 Nov 2013 21:47:20 -0800 Subject: [PATCH 0326/1179] Add changelog entries. --- Changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3e65af32f..2eb5c5221 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,11 +4,18 @@ Breaking Changes for 3.0.0: * Rename `RSpec::Mocks::Mock` to `RSpec::Mocks::Double`. (Myron Marston) +* Change how to integrate rspec-mocks in other test frameworks. You now + need to include `RSpec::Mocks::ExampleMethods` in your test context. + (Myron Marston) Bug Fixes: * Fix regression in 3.0.0.beta1 that caused `double("string_name" => :value)` to stop working. (Xavier Shay) +* Fix the way rspec-mocks and rspec-core interact so that if users + define a `let` with the same name as one of the methods + from `RSpec::Mocks::ArgumentMatchers`, the user's `let` takes + precedence. (Michi Huber, Myron Marston) ### 3.0.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) From 69914edd6d49dcf2e1d3a46e57fcd775d7bce422 Mon Sep 17 00:00:00 2001 From: Stuart Hicks Date: Mon, 18 Nov 2013 09:50:25 +0000 Subject: [PATCH 0327/1179] Update features README to include same clause specifying optional identifier parameter for doubles() --- features/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/README.md b/features/README.md index 62df93e89..b70a58552 100644 --- a/features/README.md +++ b/features/README.md @@ -15,7 +15,7 @@ invokes a _method_ with the same name as the message. ## Test Doubles A test double is an object that stands in for another object in your system -during a code example. Use the `double` method to create one: +during a code example. Use the `double` method, passing in an optional identifier, to create one: book = double("book") From 3ddd8ab723241724f0978a43267a801276530e95 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 13 Nov 2013 17:54:50 -0500 Subject: [PATCH 0328/1179] Add allow(...).to receive_message_chain --- Changelog.md | 6 + .../message_chains_using_expect.feature | 49 +++++ .../mocks/any_instance/expect_chain_chain.rb | 35 ++++ lib/rspec/mocks/any_instance/recorder.rb | 9 + .../mocks/any_instance/stub_chain_chain.rb | 4 + lib/rspec/mocks/error_generator.rb | 6 +- lib/rspec/mocks/framework.rb | 4 +- lib/rspec/mocks/matchers/receive.rb | 24 +-- .../mocks/matchers/receive_message_chain.rb | 65 ++++++ lib/rspec/mocks/message_chain.rb | 91 +++++++++ lib/rspec/mocks/message_expectation.rb | 57 +++--- lib/rspec/mocks/stub_chain.rb | 51 ----- lib/rspec/mocks/syntax.rb | 36 ++++ lib/rspec/mocks/targets.rb | 20 +- spec/rspec/mocks/and_call_original_spec.rb | 10 +- .../matchers/receive_message_chain_spec.rb | 188 ++++++++++++++++++ spec/spec_helper.rb | 4 + 17 files changed, 559 insertions(+), 100 deletions(-) create mode 100644 features/message_expectations/message_chains_using_expect.feature create mode 100644 lib/rspec/mocks/any_instance/expect_chain_chain.rb create mode 100644 lib/rspec/mocks/matchers/receive_message_chain.rb create mode 100644 lib/rspec/mocks/message_chain.rb delete mode 100644 lib/rspec/mocks/stub_chain.rb create mode 100644 spec/rspec/mocks/matchers/receive_message_chain_spec.rb diff --git a/Changelog.md b/Changelog.md index 3e65af32f..9246368fe 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,12 @@ Bug Fixes: * Fix regression in 3.0.0.beta1 that caused `double("string_name" => :value)` to stop working. (Xavier Shay) +Enhancements: + +* Add receive_message_chain which provides the functionality of the old + stub_chain for the new allow/expect syntax. Use it like so: allow(...).to + receive_message_chain(:foo, :bar, :bazz). (Sam Phippen). + ### 3.0.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) diff --git a/features/message_expectations/message_chains_using_expect.feature b/features/message_expectations/message_chains_using_expect.feature new file mode 100644 index 000000000..2beb9e3d8 --- /dev/null +++ b/features/message_expectations/message_chains_using_expect.feature @@ -0,0 +1,49 @@ +Feature: Message chains in the expect syntax + + You can use `receive_message_chain` to stub nested calls + on both partial and pure mock objects. + + Scenario: allow a chained message + Given a file named "spec/chained_messages.rb" with: + """ruby + describe "a chained message expectation" do + it "passes if the expected number of calls happen" do + d = double + allow(d).to receive_message_chain(:to_a, :length) + + d.to_a.length + end + end + """ + When I run `rspec spec/chained_messages.rb` + Then the output should contain "1 example, 0 failures" + + Scenario: allow a chained message with a return value + Given a file named "spec/chained_messages.rb" with: + """ruby + describe "a chained message expectation" do + it "passes if the expected number of calls happen" do + d = double + allow(d).to receive_message_chain(:to_a, :length).and_return(3) + + expect(d.to_a.length).to eq(3) + end + end + """ + When I run `rspec spec/chained_messages.rb` + Then the output should contain "1 example, 0 failures" + + Scenario: expect a chained message with a return value + Given a file named "spec/chained_messages.rb" with: + """ruby + describe "a chained message expectation" do + it "passes if the expected number of calls happen" do + d = double + expect(d).to receive_message_chain(:to_a, :length).and_return(3) + + expect(d.to_a.length).to eq(3) + end + end + """ + When I run `rspec spec/chained_messages.rb` + Then the output should contain "1 example, 0 failures" diff --git a/lib/rspec/mocks/any_instance/expect_chain_chain.rb b/lib/rspec/mocks/any_instance/expect_chain_chain.rb new file mode 100644 index 000000000..da856efe4 --- /dev/null +++ b/lib/rspec/mocks/any_instance/expect_chain_chain.rb @@ -0,0 +1,35 @@ +module RSpec + module Mocks + module AnyInstance + # @private + class ExpectChainChain < StubChain + def initialize(*args) + super + @expectation_fulfilled = false + end + + def expectation_fulfilled? + @expectation_fulfilled + end + + def playback!(instance) + super.tap { @expectation_fulfilled = true } + end + + private + + def create_message_expectation_on(instance) + ::RSpec::Mocks::ExpectChain.expect_chain_on(instance, *@expectation_args, &@expectation_block) + end + + def invocation_order + @invocation_order ||= { + :and_return => [nil], + :and_raise => [nil], + :and_yield => [nil] + } + end + end + end + end +end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 9aa842f3b..1a1b7eb20 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -49,6 +49,15 @@ def stub_chain(*method_names_and_optional_return_values, &block) end end + # @api private + def expect_chain(*method_names_and_optional_return_values, &block) + @expectation_set = true + normalize_chain(*method_names_and_optional_return_values) do |method_name, args| + observe!(method_name) + message_chains.add(method_name, ExpectChainChain.new(self, *args, &block)) + end + end + # Initializes the recording a message expectation to be played back # against any instance of this object that invokes the submitted # method. diff --git a/lib/rspec/mocks/any_instance/stub_chain_chain.rb b/lib/rspec/mocks/any_instance/stub_chain_chain.rb index e16d92af8..d24cfc6c0 100644 --- a/lib/rspec/mocks/any_instance/stub_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain_chain.rb @@ -3,6 +3,10 @@ module Mocks module AnyInstance # @private class StubChainChain < StubChain + def initialize(*args) + super + @expectation_fulfilled = false + end private diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 2a21e9d2e..5c3c24321 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -117,9 +117,9 @@ def raise_wrong_arity_error(args_to_yield, arity) end # @private - def raise_only_valid_on_a_partial_mock(method) - __raise "#{intro} is a pure mock object. `#{method}` is only " + - "available on a partial mock object." + def raise_only_valid_on_a_partial_double(method) + __raise "#{intro} is a pure test double. `#{method}` is only " + + "available on a partial double." end # @private diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 2a38b85bb..050d0650b 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -21,6 +21,7 @@ require 'rspec/mocks/any_instance/chain' require 'rspec/mocks/any_instance/stub_chain' require 'rspec/mocks/any_instance/stub_chain_chain' +require 'rspec/mocks/any_instance/expect_chain_chain' require 'rspec/mocks/any_instance/expectation_chain' require 'rspec/mocks/any_instance/message_chains' require 'rspec/mocks/any_instance/recorder' @@ -28,7 +29,8 @@ require 'rspec/mocks/matchers/have_received' require 'rspec/mocks/matchers/receive' require 'rspec/mocks/matchers/receive_messages' -require 'rspec/mocks/stub_chain' +require 'rspec/mocks/matchers/receive_message_chain' +require 'rspec/mocks/message_chain' require 'rspec/mocks/targets' require 'rspec/mocks/syntax' require 'rspec/mocks/configuration' diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index d616a8508..1b5af2d98 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -27,7 +27,7 @@ def setup_expectation(subject, &block) def setup_negative_expectation(subject, &block) # ensure `never` goes first for cases like `never.and_return(5)`, # where `and_return` is meant to raise an error - @recorded_customizations.unshift Customization.new(:never, [], nil) + @recorded_customizations.unshift ExpectationCustomization.new(:never, [], nil) warn_if_any_instance("expect", subject) @@ -56,7 +56,7 @@ def setup_any_instance_allowance(subject, &block) next if method_defined?(method) define_method(method) do |*args, &block| - @recorded_customizations << Customization.new(method, args, block) + @recorded_customizations << ExpectationCustomization.new(method, args, block) self end end @@ -93,18 +93,18 @@ def setup_method_substitute(host, method, block, *args) end expectation end + end + end - class Customization - def initialize(method_name, args, block) - @method_name = method_name - @args = args - @block = block - end + class ExpectationCustomization + def initialize(method_name, args, block) + @method_name = method_name + @args = args + @block = block + end - def playback_onto(expectation) - expectation.__send__(@method_name, *@args, &@block) - end - end + def playback_onto(expectation) + expectation.__send__(@method_name, *@args, &@block) end end end diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb new file mode 100644 index 000000000..bb54725f5 --- /dev/null +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -0,0 +1,65 @@ +module RSpec + module Mocks + module Matchers + #@api private + class ReceiveMessageChain + def initialize(chain, &block) + @chain = chain + @block = block + @recorded_customizations = [] + end + + [:and_return, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg| + define_method(msg) do |*args, &block| + @recorded_customizations << ExpectationCustomization.new(msg, args, block) + self + end + end + + def name + "receive_message_chain" + end + + def setup_allowance(subject, &block) + chain = StubChain.stub_chain_on(subject, *@chain, &(@block || block)) + replay_customizations(chain) + end + + def setup_any_instance_allowance(subject, &block) + recorder = ::RSpec::Mocks.any_instance_recorder_for(subject) + chain = recorder.stub_chain(*@chain, &(@block || block)) + replay_customizations(chain) + end + + def setup_any_instance_expectation(subject, &block) + recorder = ::RSpec::Mocks.any_instance_recorder_for(subject) + chain = recorder.expect_chain(*@chain, &(@block || block)) + replay_customizations(chain) + end + + def setup_expectation(subject, &block) + chain = ExpectChain.expect_chain_on(subject, *@chain, &(@block || block)) + replay_customizations(chain) + end + + def setup_negative_expectation(*args) + raise NegationUnsupportedError.new( + "`expect(...).not_to receive_message_chain` is not supported " + + "since it doesn't really make sense. What would it even mean?" + ) + end + + alias matches? setup_expectation + alias does_not_match? setup_negative_expectation + + private + + def replay_customizations(chain) + @recorded_customizations.each do |customization| + customization.playback_onto(chain) + end + end + end + end + end +end diff --git a/lib/rspec/mocks/message_chain.rb b/lib/rspec/mocks/message_chain.rb new file mode 100644 index 000000000..97f2762a1 --- /dev/null +++ b/lib/rspec/mocks/message_chain.rb @@ -0,0 +1,91 @@ +module RSpec + module Mocks + # @private + class MessageChain + attr_reader :object, :chain, :block + + def initialize(object, *chain, &blk) + @object = object + @chain, @block = format_chain(*chain, &blk) + end + + # @api private + def setup_chain + if chain.length > 1 + if matching_stub = find_matching_stub + chain.shift + chain_on(matching_stub.invoke(nil), *chain, &@block) + elsif matching_expectation = find_matching_expectation + chain.shift + chain_on(matching_expectation.invoke_without_incrementing_received_count(nil), *chain, &@block) + else + next_in_chain = Double.new + expectation(object, chain.shift, next_in_chain) + chain_on(next_in_chain, *chain, &@block) + end + else + ::RSpec::Mocks.allow_message(object, chain.shift, {}, &block) + end + end + + private + + def expectation(object, message, returned_object) + raise NotImplementedError.new + end + + def chain_on(object, *chain, &block) + initialize(object, *chain, &block) + setup_chain + end + + def format_chain(*chain, &blk) + if Hash === chain.last + hash = chain.pop + hash.each do |k,v| + chain << k + blk = lambda { v } + end + end + return chain.join('.').split('.'), blk + end + + def find_matching_stub + ::RSpec::Mocks.proxy_for(object). + __send__(:find_matching_method_stub, chain.first.to_sym) + end + + def find_matching_expectation + ::RSpec::Mocks.proxy_for(object). + __send__(:find_matching_expectation, chain.first.to_sym) + end + end + + # @private + class ExpectChain < MessageChain + # @api private + def self.expect_chain_on(object, *chain, &blk) + new(object, *chain, &blk).setup_chain + end + + private + + def expectation(object, message, returned_object) + ::RSpec::Mocks.expect_message(object, message, {}) { returned_object } + end + end + + # @private + class StubChain < MessageChain + def self.stub_chain_on(object, *chain, &blk) + new(object, *chain, &blk).setup_chain + end + + private + + def expectation(object, message, returned_object) + ::RSpec::Mocks.allow_message(object, message, {}) { returned_object } + end + end + end +end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 15895010c..46a376685 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -139,7 +139,7 @@ def yield_receiver_to_implementation_block? # Tells the object to delegate to the original unmodified method # when it receives the message. # - # @note This is only available on partial mock objects. + # @note This is only available on partial doubles. # # @example # @@ -149,7 +149,7 @@ def yield_receiver_to_implementation_block? # expect(counter.count).to eq(original_count + 1) def and_call_original if RSpec::Mocks::TestDouble === @method_double.object - @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original) + @error_generator.raise_only_valid_on_a_partial_double(:and_call_original) else if implementation.inner_action RSpec.warning("You're overriding a previous implementation for this stub") @@ -224,29 +224,12 @@ def matches?(message, *args) # @private def invoke(parent_stub, *args, &block) - if yield_receiver_to_implementation_block? - args.unshift(orig_object) - end - - if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count)) - @actual_received_count += 1 - @failed_fast = true - #args are the args we actually received, @argument_list_matcher is the - #list of args we were expecting - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args) - end - - @order_group.handle_order_constraint self + invoke_incrementing_actual_calls_by(1, parent_stub, *args, &block) + end - begin - if implementation.present? - implementation.call(*args, &block) - elsif parent_stub - parent_stub.invoke(nil, *args, &block) - end - ensure - @actual_received_count += 1 - end + # @private + def invoke_without_incrementing_received_count(parent_stub, *args, &block) + invoke_incrementing_actual_calls_by(0, parent_stub, *args, &block) end # @private @@ -495,6 +478,32 @@ def increase_actual_received_count! private + def invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block) + if yield_receiver_to_implementation_block? + args.unshift(orig_object) + end + + if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count)) + @actual_received_count += increment + @failed_fast = true + #args are the args we actually received, @argument_list_matcher is the + #list of args we were expecting + @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args) + end + + @order_group.handle_order_constraint self + + begin + if implementation.present? + implementation.call(*args, &block) + elsif parent_stub + parent_stub.invoke(nil, *args, &block) + end + ensure + @actual_received_count += increment + end + end + def failed_fast? @failed_fast end diff --git a/lib/rspec/mocks/stub_chain.rb b/lib/rspec/mocks/stub_chain.rb deleted file mode 100644 index e45e0b8d1..000000000 --- a/lib/rspec/mocks/stub_chain.rb +++ /dev/null @@ -1,51 +0,0 @@ -module RSpec - module Mocks - # @private - class StubChain - def self.stub_chain_on(object, *chain, &blk) - new(object, *chain, &blk).stub_chain - end - - attr_reader :object, :chain, :block - - def initialize(object, *chain, &blk) - @object = object - @chain, @block = format_chain(*chain, &blk) - end - - def stub_chain - if chain.length > 1 - if matching_stub = find_matching_stub - chain.shift - matching_stub.invoke(nil).stub_chain(*chain, &block) - else - next_in_chain = Double.new - object.stub(chain.shift) { next_in_chain } - StubChain.stub_chain_on(next_in_chain, *chain, &block) - end - else - object.stub(chain.shift, &block) - end - end - - private - - def format_chain(*chain, &blk) - if Hash === chain.last - hash = chain.pop - hash.each do |k,v| - chain << k - blk = lambda { v } - end - end - return chain.join('.').split('.'), blk - end - - def find_matching_stub - ::RSpec::Mocks.proxy_for(object). - __send__(:find_matching_method_stub, chain.first.to_sym) - end - end - end -end - diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 3d0714c04..4a75a7b0e 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -124,6 +124,10 @@ def receive_messages(message_return_value_hash) matcher end + def receive_message_chain(*messages, &block) + Matchers::ReceiveMessageChain.new(messages, &block) + end + def allow(target) AllowanceTarget.new(target) end @@ -152,6 +156,7 @@ def self.disable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods) syntax_host.class_exec do undef receive undef receive_messages + undef receive_message_chain undef allow undef expect_any_instance_of undef allow_any_instance_of @@ -368,6 +373,37 @@ def self.default_should_syntax_host # allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow") # # @note This is only available when you have enabled the `expect` syntax. + # + # @method receive_message_chain + # @overload receive_message_chain(method1, method2) + # @overload receive_message_chain("method1.method2") + # @overload receive_message_chain(method1, method_to_value_hash) + # + # stubs/mocks a chain of messages on an object or test double. + # + # ## Warning: + # + # Chains can be arbitrarily long, which makes it quite painless to + # violate the Law of Demeter in violent ways, so you should consider any + # use of `receive_message_chain` a code smell. Even though not all code smells + # indicate real problems (think fluent interfaces), `receive_message_chain` still + # results in brittle examples. For example, if you write + # `foo.receive_message_chain(:bar, :baz => 37)` in a spec and then the + # implementation calls `foo.baz.bar`, the stub will not work. + # + # @example + # + # allow(double).to receive_message_chain("foo.bar") { :baz } + # allow(double).to receive_message_chain(:foo, :bar => :baz) + # allow(double).to receive_message_chain(:foo, :bar) { :baz } + # + # # Given any of ^^ these three forms ^^: + # double.foo.bar # => :baz + # + # # Common use in Rails/ActiveRecord: + # allow(Article).to receive_message_chain("recent.published") { [Article.new] } + # + # @note This is only available when you have enabled the `expect` syntax. end end end diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 9c949d457..00c75309c 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -10,7 +10,7 @@ def initialize(target) def self.delegate_to(matcher_method) define_method(:to) do |matcher, &block| - unless Matchers::Receive === matcher || Matchers::ReceiveMessages === matcher + unless matcher_allowed?(matcher) raise_unsupported_matcher(:to, matcher) end define_matcher(matcher, matcher_method, &block) @@ -21,8 +21,10 @@ def self.delegate_not_to(matcher_method, options = {}) method_name = options.fetch(:from) define_method(method_name) do |matcher, &block| case matcher - when Matchers::Receive then define_matcher(matcher, matcher_method, &block) - when Matchers::ReceiveMessages then raise_negation_unsupported(method_name, matcher) + when Matchers::Receive + define_matcher(matcher, matcher_method, &block) + when Matchers::ReceiveMessages, Matchers::ReceiveMessageChain + raise_negation_unsupported(method_name, matcher) else raise_unsupported_matcher(method_name, matcher) end @@ -37,6 +39,17 @@ def self.disallow_negation(method_name) private + def matcher_allowed?(matcher) + ALLOWED_MATCHERS.include?(matcher.class) + end + + #@api private + ALLOWED_MATCHERS = [ + Matchers::Receive, + Matchers::ReceiveMessages, + Matchers::ReceiveMessageChain, + ] + def define_matcher(matcher, name, &block) matcher.__send__(name, @target, &block) end @@ -87,4 +100,3 @@ class AnyInstanceExpectationTarget < TargetBase end end end - diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 073fc96f9..a1524df5a 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -2,7 +2,7 @@ require 'delegate' describe "and_call_original" do - context "on a partial mock object" do + context "on a partial double" do let(:klass) do Class.new do def meth_1 @@ -200,7 +200,7 @@ def method_missing(name, *args) end end - context "on a partial mock object that overrides #method" do + context "on a partial double that overrides #method" do let(:request_klass) do Struct.new(:method, :url) do def perform @@ -230,17 +230,17 @@ def request.perform end end - context "on a pure mock object" do + context "on a pure test double" do let(:instance) { double } - it 'raises an error even if the mock object responds to the message' do + it 'raises an error even if the double object responds to the message' do expect(instance.to_s).to be_a(String) mock_expectation = instance.should_receive(:to_s) instance.to_s # to satisfy the expectation expect { mock_expectation.and_call_original - }.to raise_error(/and_call_original.*partial mock/i) + }.to raise_error(/pure test double.*and_call_original.*partial double/i) end end end diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb new file mode 100644 index 000000000..fb17eb781 --- /dev/null +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -0,0 +1,188 @@ +require "spec_helper" + + +module RSpec::Mocks::Matchers + describe "receive_message_chain" do + let(:object) { double(:object) } + + context "with only the expect syntax enabled" do + include_context "with syntax", :expect + + it "errors with a negative allowance" do + expect { + allow(object).not_to receive_message_chain(:to_a) + }.to raise_error(RSpec::Mocks::NegationUnsupportedError) + end + + it "errors with a negative expectation" do + expect { + expect(object).not_to receive_message_chain(:to_a) + }.to raise_error(RSpec::Mocks::NegationUnsupportedError) + end + + it "errors with a negative any_instance expectation" do + expect { + expect_any_instance_of(Object).not_to receive_message_chain(:to_a) + }.to raise_error(RSpec::Mocks::NegationUnsupportedError) + end + + it "errors with a negative any_instance allowance" do + expect { + allow_any_instance_of(Object).not_to receive_message_chain(:to_a) + }.to raise_error(RSpec::Mocks::NegationUnsupportedError) + end + + it "works with a do block" do + allow(object).to receive_message_chain(:to_a, :length) do + 3 + end + + expect(object.to_a.length).to eq(3) + end + + it "works with a {} block" do + allow(object).to receive_message_chain(:to_a, :length) { 3 } + + expect(object.to_a.length).to eq(3) + end + + it "gives the { } block prescedence over the do block" do + allow(object).to receive_message_chain(:to_a, :length) { 3 } do + 4 + end + + expect(object.to_a.length).to eq(3) + end + + it "works with and_return" do + allow(object).to receive_message_chain(:to_a, :length).and_return(3) + + expect(object.to_a.length).to eq(3) + end + + it "works with and_call_original", :pending => "See https://github.com/rspec/rspec-mocks/pull/467#issuecomment-28631621" do + list = [1, 2, 3] + expect(list).to receive_message_chain(:to_a, :length).and_call_original + expect(list.to_a.length).to eq(3) + end + + it "fails with and_call_original when the entire chain is not called", :pending => "See https://github.com/rspec/rspec-mocks/pull/467#issuecomment-28631621" do + list = [1, 2, 3] + expect(list).to receive_message_chain(:to_a, :length).and_call_original + expect(list.to_a).to eq([1, 2, 3]) + end + + it "works with and_raise" do + allow(object).to receive_message_chain(:to_a, :length).and_raise(StandardError.new("hi")) + + expect { object.to_a.length }.to raise_error(StandardError, "hi") + end + + it "works with and_throw" do + allow(object).to receive_message_chain(:to_a, :length).and_throw(:nope) + + expect { object.to_a.length }.to throw_symbol(:nope) + end + + it "works with and_yield" do + allow(object).to receive_message_chain(:to_a, :length).and_yield(3) + + expect { |blk| object.to_a.length(&blk) }.to yield_with_args(3) + end + + it "works with a string of messages to chain" do + allow(object).to receive_message_chain("to_a.length").and_yield(3) + + expect { |blk| object.to_a.length(&blk) }.to yield_with_args(3) + end + + it "works with a hash return as the last argument in the chain" do + allow(object).to receive_message_chain(:to_a, :length => 3) + + expect(object.to_a.length).to eq(3) + end + + it "raises when expect is used and the entire chain isn't called" do + expect { + expect(object).to receive_message_chain(:to_a, :farce, :length => 3) + object.to_a + verify_all + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "does not raise when expect is used and the entire chain is called" do + expect { + expect(object).to receive_message_chain(:to_a, :length => 3) + object.to_a.length + verify_all + }.not_to raise_error + end + + it "works with allow_any_instance" do + o = Object.new + + allow_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) + + expect(o.to_a.length).to eq(3) + end + + it "fails when with expect_any_instance_of is used and the entire chain is not called" do + o = Object.new + + expect { + expect_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) + verify_all + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "passes when with expect_any_instance_of is used and the entire chain is called" do + o = Object.new + + expect_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) + o.to_a.length + end + + it "works with expect where the first level of the chain is already expected" do + o = Object.new + expect(o).to receive(:foo).and_return(double) + expect(o).to receive_message_chain(:foo, :bar, :baz) + + o.foo.bar.baz + end + + it "works with allow where the first level of the chain is already expected" do + o = Object.new + expect(o).to receive(:foo).and_return(double) + allow(o).to receive_message_chain(:foo, :bar, :baz).and_return(3) + + expect(o.foo.bar.baz).to eq(3) + end + + it "works with expect where the first level of the chain is already stubbed" do + o = Object.new + allow(o).to receive(:foo).and_return(double) + expect(o).to receive_message_chain(:foo, :bar, :baz) + + o.foo.bar.baz + end + + it "works with allow where the first level of the chain is already stubbed" do + o = Object.new + allow(o).to receive(:foo).and_return(double) + allow(o).to receive_message_chain(:foo, :bar, :baz).and_return(3) + + expect(o.foo.bar.baz).to eq(3) + end + end + + context "when the expect and should syntaxes are enabled" do + include_context "with syntax", [:expect, :should] + + it "stubs the message correctly" do + allow(object).to receive_message_chain(:to_a, :length) + + expect { object.to_a.length }.not_to raise_error + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5fcb4c4fe..9a40070db 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,6 +27,10 @@ def verify(object) def reset(object) RSpec::Mocks.proxy_for(object).reset end + + def verify_all + RSpec::Mocks.space.verify_all + end end module DeprecationHelpers From 6d68428bca8af286828f61b283abf1704f9e55e9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 19 Nov 2013 21:52:26 -0800 Subject: [PATCH 0329/1179] Remove unneeded `initialize` definitions/args. --- lib/rspec/mocks/argument_matchers.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index dd51d4a15..740d2a5c1 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -18,9 +18,6 @@ def description end class AnyArgMatcher - def initialize(ignore) - end - def ==(other) true end @@ -43,9 +40,6 @@ def ==(value) end class BooleanMatcher - def initialize(ignore) - end - def ==(value) [true,false].include?(value) end @@ -163,7 +157,7 @@ def any_args # # object.should_receive(:message).with(anything) def anything - AnyArgMatcher.new(nil) + AnyArgMatcher.new end # Matches no arguments. @@ -191,7 +185,7 @@ def duck_type(*args) # # object.should_receive(:message).with(boolean()) def boolean - BooleanMatcher.new(nil) + BooleanMatcher.new end # Matches a hash that includes the specified key(s) or key/value pairs. @@ -255,7 +249,7 @@ def kind_of(klass) # @api private def self.anythingize_lonely_keys(*args) hash = args.last.class == Hash ? args.delete_at(-1) : {} - args.each { | arg | hash[arg] = AnyArgMatcher.new(nil) } + args.each { | arg | hash[arg] = AnyArgMatcher.new } hash end end From 2b3ddf4a76fc12d1bc0efb9d5299b2217ec66030 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 19 Nov 2013 21:54:44 -0800 Subject: [PATCH 0330/1179] Unwrap unnecessary Array#include?. - The old version created an extra object (the array). - It also involved more message sends: - Array#include? - `==` to each element For a static list of 2 items, it will perform better to just compare each item individually. --- lib/rspec/mocks/argument_matchers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 740d2a5c1..1cce2b7f8 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -41,7 +41,7 @@ def ==(value) class BooleanMatcher def ==(value) - [true,false].include?(value) + true == value || false == value end end From 83d34c3cd03c1665c2e64e5acb0dbb062e46a481 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 19 Nov 2013 22:02:39 -0800 Subject: [PATCH 0331/1179] Remove support for ArgumentListMatcher block. This feature was removed in #426, but this bit of the implementation of that feature remained. --- lib/rspec/mocks/argument_list_matcher.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index 6959994a1..e096e2b59 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -40,9 +40,8 @@ class ArgumentListMatcher # # @see ArgumentMatchers # @see #args_match? - def initialize(*expected_args, &block) + def initialize(*expected_args) @expected_args = expected_args - @block = expected_args.empty? ? block : nil @match_any_args = false @matchers = nil @@ -64,7 +63,7 @@ def initialize(*expected_args, &block) # # @see #initialize def args_match?(*args) - match_any_args? || block_passes?(*args) || matchers_match?(*args) + match_any_args? || matchers_match?(*args) end private @@ -83,10 +82,6 @@ def is_matcher?(object) end && object.respond_to?(:matches?) end - def block_passes?(*args) - @block.call(*args) if @block - end - def matchers_match?(*args) @matchers == args end From 75c28786ca75055e1c25ac7850baa295a9a0d01f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 19 Nov 2013 22:07:53 -0800 Subject: [PATCH 0332/1179] Switch arg matchers to use `===` rather than `==`. * `==` should be symmetric; `a == b` and `b == a` should return the same value. This wasn't the case before. * `===` need not be symmetric; it's designed for matching in case statements, which matches the semantics we want. * `===` opens up new avenues of flexibility (e.g. lambdas can be passed on 1.9+ as an arg matcher since they implement `===`). * This allows us to simplify our logic and remove some classes. --- lib/rspec/mocks/argument_list_matcher.rb | 9 ++- lib/rspec/mocks/argument_matchers.rb | 79 +++++++------------ .../mocks/array_including_matcher_spec.rb | 12 +-- .../mocks/failing_argument_matchers_spec.rb | 26 ++++++ .../mocks/hash_excluding_matcher_spec.rb | 24 +++--- .../mocks/hash_including_matcher_spec.rb | 38 +++++---- .../mocks/passing_argument_matchers_spec.rb | 18 +++++ 7 files changed, 118 insertions(+), 88 deletions(-) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index e096e2b59..d8069a13c 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -70,8 +70,7 @@ def args_match?(*args) def matcher_for(arg) return ArgumentMatchers::MatcherMatcher.new(arg) if is_matcher?(arg) - return ArgumentMatchers::RegexpMatcher.new(arg) if Regexp === arg - return ArgumentMatchers::EqualityProxy.new(arg) + arg end def is_matcher?(object) @@ -83,7 +82,11 @@ def is_matcher?(object) end def matchers_match?(*args) - @matchers == args + return false unless @matchers.count == args.count + + @matchers.zip(args).all? do |(matcher, arg)| + ArgumentMatchers.values_match?(matcher, arg) + end end def match_any_args? diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 1cce2b7f8..2220fe420 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -10,6 +10,9 @@ module Mocks # # @see ArgumentListMatcher module ArgumentMatchers + def self.values_match?(expected, actual) + expected === actual || actual == expected + end class AnyArgsMatcher def description @@ -18,7 +21,7 @@ def description end class AnyArgMatcher - def ==(other) + def ===(other) true end end @@ -29,51 +32,47 @@ def description end end - class RegexpMatcher - def initialize(regexp) - @regexp = regexp - end - - def ==(value) - Regexp === value ? value == @regexp : value =~ @regexp - end - end - class BooleanMatcher - def ==(value) + def ===(value) true == value || false == value end end - class HashIncludingMatcher + class BaseHashMatcher def initialize(expected) @expected = expected end - def ==(actual) - @expected.all? {|k,v| actual.has_key?(k) && v == actual[k]} + def ===(predicate, actual) + @expected.__send__(predicate) do |k, v| + actual.has_key?(k) && ArgumentMatchers.values_match?(v, actual[k]) + end rescue NoMethodError false end - def description - "hash_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})" + def description(name) + "#{name}(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})" end end - class HashExcludingMatcher - def initialize(expected) - @expected = expected + class HashIncludingMatcher < BaseHashMatcher + def ===(actual) + super(:all?, actual) end - def ==(actual) - @expected.none? {|k,v| actual.has_key?(k) && v == actual[k]} - rescue NoMethodError - false + def description + super("hash_including") + end + end + + class HashExcludingMatcher < BaseHashMatcher + def ===(actual) + super(:none?, actual) end def description - "hash_not_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})" + super("hash_not_including") end end @@ -82,7 +81,7 @@ def initialize(expected) @expected = expected end - def ==(actual) + def ===(actual) Set.new(actual).superset?(Set.new(@expected)) end @@ -96,7 +95,7 @@ def initialize(*methods_to_respond_to) @methods_to_respond_to = methods_to_respond_to end - def ==(value) + def ===(value) @methods_to_respond_to.all? {|message| value.respond_to?(message)} end end @@ -106,41 +105,21 @@ def initialize(matcher) @matcher = matcher end - def ==(value) + def ===(value) @matcher.matches?(value) end end - class EqualityProxy - def initialize(given) - @given = given - end - - def ==(expected) - @given == expected - end - end - class InstanceOf def initialize(klass) @klass = klass end - def ==(actual) + def ===(actual) actual.instance_of?(@klass) end end - class KindOf - def initialize(klass) - @klass = klass - end - - def ==(actual) - actual.kind_of?(@klass) - end - end - # Matches any args at all. Supports a more explicit variation of # `object.should_receive(:message)` # @@ -241,7 +220,7 @@ def instance_of(klass) # # object.should_receive(:message).with(kind_of(Thing)) def kind_of(klass) - KindOf.new(klass) + klass end alias_method :a_kind_of, :kind_of diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb index 2f9046de8..e038de322 100644 --- a/spec/rspec/mocks/array_including_matcher_spec.rb +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -10,29 +10,29 @@ module ArgumentMatchers context "passing" do it "matches the same array" do - expect(array_including(1, 2, 3)).to eq([1, 2, 3]) + expect(array_including(1, 2, 3)).to be === [1, 2, 3] end it "matches the same array, specified without square brackets" do - expect(array_including(1, 2, 3)).to eq([1, 2, 3]) + expect(array_including(1, 2, 3)).to be === [1, 2, 3] end it "matches the same array, which includes nested arrays" do - expect(array_including([1, 2], 3, 4)).to eq([[1, 2], 3, 4]) + expect(array_including([1, 2], 3, 4)).to be === [[1, 2], 3, 4] end it "works with duplicates in expected" do - expect(array_including(1, 1, 2, 3)).to eq([1, 2, 3]) + expect(array_including(1, 1, 2, 3)).to be === [1, 2, 3] end it "works with duplicates in actual" do - expect(array_including(1, 2, 3)).to eq([1, 1, 2, 3]) + expect(array_including(1, 2, 3)).to be === [1, 1, 2, 3] end end context "failing" do it "fails when not all the entries in the expected are present" do - expect(array_including(1,2,3,4,5)).not_to eq([1,2]) + expect(array_including(1,2,3,4,5)).not_to be === [1,2] end end end diff --git a/spec/rspec/mocks/failing_argument_matchers_spec.rb b/spec/rspec/mocks/failing_argument_matchers_spec.rb index 442768f79..014085ea6 100644 --- a/spec/rspec/mocks/failing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/failing_argument_matchers_spec.rb @@ -103,6 +103,17 @@ module Mocks end.to raise_error(ArgumentError, /must have at least one argument/) end + it "fails when given an arbitrary object that returns false from #===" do + matcher = double + matcher.should_receive(:===).with(4).at_least(:once).and_return(false) + + @double.should_receive(:foo).with(matcher) + + expect { + @double.foo(4) + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + it "fails with sensible message when args respond to #description" do arg = Class.new do def description @@ -151,6 +162,21 @@ def inspect @double.msg 3 end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (my_thing)\n got: (3)") end + + it "fails a class against an unrelated class" do + @double.should_receive(:foo).with(Fixnum) + expect { + @double.foo(Hash) + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "fails a class against an object of a different type" do + @double.should_receive(:foo).with(Fixnum) + + expect { + @double.foo(3.2) + }.to raise_error(RSpec::Mocks::MockExpectationError) + end end end end diff --git a/spec/rspec/mocks/hash_excluding_matcher_spec.rb b/spec/rspec/mocks/hash_excluding_matcher_spec.rb index c2a90d812..1eddff2ea 100644 --- a/spec/rspec/mocks/hash_excluding_matcher_spec.rb +++ b/spec/rspec/mocks/hash_excluding_matcher_spec.rb @@ -11,54 +11,54 @@ module ArgumentMatchers describe "passing" do it "matches a hash without the specified key" do - expect(hash_not_including(:c)).to eq({:a => 1, :b => 2}) + expect(hash_not_including(:c)).to be === {:a => 1, :b => 2} end it "matches a hash with the specified key, but different value" do - expect(hash_not_including(:b => 3)).to eq({:a => 1, :b => 2}) + expect(hash_not_including(:b => 3)).to be === {:a => 1, :b => 2} end it "matches a hash without the specified key, given as anything()" do - expect(hash_not_including(:c => anything)).to eq({:a => 1, :b => 2}) + expect(hash_not_including(:c => anything)).to be === {:a => 1, :b => 2} end it "matches an empty hash" do - expect(hash_not_including(:a)).to eq({}) + expect(hash_not_including(:a)).to be === {} end it "matches a hash without any of the specified keys" do - expect(hash_not_including(:a, :b, :c)).to eq(:d => 7) + expect(hash_not_including(:a, :b, :c)).to be === { :d => 7 } end end describe "failing" do it "does not match a non-hash" do - expect(hash_not_including(:a => 1)).not_to eq 1 + expect(hash_not_including(:a => 1)).not_to be === 1 end it "does not match a hash with a specified key" do - expect(hash_not_including(:b)).not_to eq(:b => 2) + expect(hash_not_including(:b)).not_to be === { :b => 2 } end it "does not match a hash with the specified key/value pair" do - expect(hash_not_including(:b => 2)).not_to eq(:a => 1, :b => 2) + expect(hash_not_including(:b => 2)).not_to be === { :a => 1, :b => 2 } end it "does not match a hash with the specified key" do - expect(hash_not_including(:a, :b => 3)).not_to eq(:a => 1, :b => 2) + expect(hash_not_including(:a, :b => 3)).not_to be === { :a => 1, :b => 2 } end it "does not match a hash with one of the specified keys" do - expect(hash_not_including(:a, :b)).not_to eq(:b => 2) + expect(hash_not_including(:a, :b)).not_to be === { :b => 2 } end it "does not match a hash with some of the specified keys" do - expect(hash_not_including(:a, :b, :c)).not_to eq(:a => 1, :b => 2) + expect(hash_not_including(:a, :b, :c)).not_to be === { :a => 1, :b => 2 } end it "does not match a hash with one key/value pair included" do - expect(hash_not_including(:a, :b, :c, :d => 7)).not_to eq(:d => 7) + expect(hash_not_including(:a, :b, :c, :d => 7)).not_to be === { :d => 7 } end end end diff --git a/spec/rspec/mocks/hash_including_matcher_spec.rb b/spec/rspec/mocks/hash_including_matcher_spec.rb index 999bcc8c8..0ea90df1d 100644 --- a/spec/rspec/mocks/hash_including_matcher_spec.rb +++ b/spec/rspec/mocks/hash_including_matcher_spec.rb @@ -11,77 +11,81 @@ module ArgumentMatchers describe "passing" do it "matches the same hash" do - expect(hash_including(:a => 1)).to eq({:a => 1}) + expect(hash_including(:a => 1)).to be === {:a => 1} end it "matches a hash with extra stuff" do - expect(hash_including(:a => 1)).to eq({:a => 1, :b => 2}) + expect(hash_including(:a => 1)).to be === {:a => 1, :b => 2} end describe "when matching against other matchers" do it "matches an int against anything()" do - expect(hash_including(:a => anything, :b => 2)).to eq({:a => 1, :b => 2}) + expect(hash_including(:a => anything, :b => 2)).to be === {:a => 1, :b => 2} end it "matches a string against anything()" do - expect(hash_including(:a => anything, :b => 2)).to eq({:a => "1", :b => 2}) + expect(hash_including(:a => anything, :b => 2)).to be === {:a => "1", :b => 2} + end + + it 'can match against arbitrary objects that implement #===' do + expect(hash_including(:a => /foo/)).to be === { :a => "foobar" } end end describe "when passed only keys or keys mixed with key/value pairs" do it "matches if the key is present" do - expect(hash_including(:a)).to eq({:a => 1, :b => 2}) + expect(hash_including(:a)).to be === {:a => 1, :b => 2} end it "matches if more keys are present" do - expect(hash_including(:a, :b)).to eq({:a => 1, :b => 2, :c => 3}) + expect(hash_including(:a, :b)).to be === {:a => 1, :b => 2, :c => 3} end it "matches a string against a given key" do - expect(hash_including(:a)).to eq({:a => "1", :b => 2}) + expect(hash_including(:a)).to be === {:a => "1", :b => 2} end it "matches if passed one key and one key/value pair" do - expect(hash_including(:a, :b => 2)).to eq({:a => 1, :b => 2}) + expect(hash_including(:a, :b => 2)).to be === {:a => 1, :b => 2} end it "matches if passed many keys and one key/value pair" do - expect(hash_including(:a, :b, :c => 3)).to eq({:a => 1, :b => 2, :c => 3, :d => 4}) + expect(hash_including(:a, :b, :c => 3)).to be === {:a => 1, :b => 2, :c => 3, :d => 4} end it "matches if passed many keys and many key/value pairs" do - expect(hash_including(:a, :b, :c => 3, :e => 5)).to eq({:a => 1, :b => 2, :c => 3, :d => 4, :e => 5}) + expect(hash_including(:a, :b, :c => 3, :e => 5)).to be === {:a => 1, :b => 2, :c => 3, :d => 4, :e => 5} end end end describe "failing" do it "does not match a non-hash" do - expect(hash_including(:a => 1)).not_to eq 1 + expect(hash_including(:a => 1)).not_to be === 1 end it "does not match a hash with a missing key" do - expect(hash_including(:a => 1)).not_to eq(:b => 2) + expect(hash_including(:a => 1)).not_to be === { :b => 2 } end it "does not match a hash with a missing key" do - expect(hash_including(:a)).not_to eq(:b => 2) + expect(hash_including(:a)).not_to be === { :b => 2 } end it "does not match an empty hash with a given key" do - expect(hash_including(:a)).not_to eq({}) + expect(hash_including(:a)).not_to be === {} end it "does not match a hash with a missing key when one pair is matching" do - expect(hash_including(:a, :b => 2)).not_to eq(:b => 2) + expect(hash_including(:a, :b => 2)).not_to be === { :b => 2 } end it "does not match a hash with an incorrect value" do - expect(hash_including(:a => 1, :b => 2)).not_to eq(:a => 1, :b => 3) + expect(hash_including(:a => 1, :b => 2)).not_to be === { :a => 1, :b => 3 } end it "does not match when values are nil but keys are different" do - expect(hash_including(:a => nil)).not_to eq(:b => nil) + expect(hash_including(:a => nil)).not_to be === { :b => nil } end end end diff --git a/spec/rspec/mocks/passing_argument_matchers_spec.rb b/spec/rspec/mocks/passing_argument_matchers_spec.rb index 2f8dd7248..c5fad81f7 100644 --- a/spec/rspec/mocks/passing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/passing_argument_matchers_spec.rb @@ -87,6 +87,14 @@ module Mocks @double.should_receive(:random_call).with(array_including(1,2)) @double.random_call([1,2]) end + + it "matches any arbitrary object using #===" do + matcher = double + matcher.should_receive(:===).with(4).and_return(true) + + @double.should_receive(:foo).with(matcher) + @double.foo(4) + end end context "handling non-matcher arguments" do @@ -121,6 +129,16 @@ module Mocks @double.should_receive(:random_call).with(:a => "a", :b => "b") @double.random_call(opts) end + + it "matches a class against itself" do + @double.should_receive(:foo).with(Fixnum) + @double.foo(Fixnum) + end + + it "matches a class against an instance of itself" do + @double.should_receive(:foo).with(Fixnum) + @double.foo(3) + end end end end From aaf19331846d64cfd904dca50d1858f7e61a3ed1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 19 Nov 2013 22:50:42 -0800 Subject: [PATCH 0333/1179] Make it clear what the public API in this file is. - Add YARD docs to differentiate. - Move the private stuff below the public stuff so the public stuff is more prominent. --- lib/rspec/mocks/argument_matchers.rb | 241 +++++++++++++++------------ 1 file changed, 131 insertions(+), 110 deletions(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 2220fe420..daca3251e 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -10,116 +10,6 @@ module Mocks # # @see ArgumentListMatcher module ArgumentMatchers - def self.values_match?(expected, actual) - expected === actual || actual == expected - end - - class AnyArgsMatcher - def description - "any args" - end - end - - class AnyArgMatcher - def ===(other) - true - end - end - - class NoArgsMatcher - def description - "no args" - end - end - - class BooleanMatcher - def ===(value) - true == value || false == value - end - end - - class BaseHashMatcher - def initialize(expected) - @expected = expected - end - - def ===(predicate, actual) - @expected.__send__(predicate) do |k, v| - actual.has_key?(k) && ArgumentMatchers.values_match?(v, actual[k]) - end - rescue NoMethodError - false - end - - def description(name) - "#{name}(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})" - end - end - - class HashIncludingMatcher < BaseHashMatcher - def ===(actual) - super(:all?, actual) - end - - def description - super("hash_including") - end - end - - class HashExcludingMatcher < BaseHashMatcher - def ===(actual) - super(:none?, actual) - end - - def description - super("hash_not_including") - end - end - - class ArrayIncludingMatcher - def initialize(expected) - @expected = expected - end - - def ===(actual) - Set.new(actual).superset?(Set.new(@expected)) - end - - def description - "array_including(#{@expected.join(",")})" - end - end - - class DuckTypeMatcher - def initialize(*methods_to_respond_to) - @methods_to_respond_to = methods_to_respond_to - end - - def ===(value) - @methods_to_respond_to.all? {|message| value.respond_to?(message)} - end - end - - class MatcherMatcher - def initialize(matcher) - @matcher = matcher - end - - def ===(value) - @matcher.matches?(value) - end - end - - class InstanceOf - def initialize(klass) - @klass = klass - end - - def ===(actual) - actual.instance_of?(@klass) - end - end - # Matches any args at all. Supports a more explicit variation of # `object.should_receive(:message)` # @@ -231,6 +121,137 @@ def self.anythingize_lonely_keys(*args) args.each { | arg | hash[arg] = AnyArgMatcher.new } hash end + + # @api private + # Implements our matching semantics for two arbitrary values. + def self.values_match?(expected, actual) + # `===` provides the main matching semantics we want, but + # has some slight gotchas: + # + # * `Fixnum === Fixnum` returns false. + # * `/abc/ === /abc/` returns false. + # + # So, for cases like these, we check `==` as well as a fallback. + expected === actual || actual == expected + end + + # @api private + class AnyArgsMatcher + def description + "any args" + end + end + + # @api private + class AnyArgMatcher + def ===(other) + true + end + end + + # @api private + class NoArgsMatcher + def description + "no args" + end + end + + # @api private + class BooleanMatcher + def ===(value) + true == value || false == value + end + end + + # @api private + class BaseHashMatcher + def initialize(expected) + @expected = expected + end + + def ===(predicate, actual) + @expected.__send__(predicate) do |k, v| + actual.has_key?(k) && ArgumentMatchers.values_match?(v, actual[k]) + end + rescue NoMethodError + false + end + + def description(name) + "#{name}(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})" + end + end + + # @api private + class HashIncludingMatcher < BaseHashMatcher + def ===(actual) + super(:all?, actual) + end + + def description + super("hash_including") + end + end + + # @api private + class HashExcludingMatcher < BaseHashMatcher + def ===(actual) + super(:none?, actual) + end + + def description + super("hash_not_including") + end + end + + # @api private + class ArrayIncludingMatcher + def initialize(expected) + @expected = expected + end + + def ===(actual) + Set.new(actual).superset?(Set.new(@expected)) + end + + def description + "array_including(#{@expected.join(",")})" + end + end + + # @api private + class DuckTypeMatcher + def initialize(*methods_to_respond_to) + @methods_to_respond_to = methods_to_respond_to + end + + def ===(value) + @methods_to_respond_to.all? {|message| value.respond_to?(message)} + end + end + + # @api private + class MatcherMatcher + def initialize(matcher) + @matcher = matcher + end + + def ===(value) + @matcher.matches?(value) + end + end + + # @api private + class InstanceOf + def initialize(klass) + @klass = klass + end + + def ===(actual) + actual.instance_of?(@klass) + end + end + end end end From fb1d450eedd22abbb2f5cf534547cb5391276a55 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 19 Nov 2013 22:52:02 -0800 Subject: [PATCH 0334/1179] Remove unused line. This was causing a ruby warning: rspec-mocks/spec/rspec/mocks/matchers/receive_message_chain_spec.rb:130: warning: assigned but unused variable - o --- spec/rspec/mocks/matchers/receive_message_chain_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index fb17eb781..0d2eac20c 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -127,8 +127,6 @@ module RSpec::Mocks::Matchers end it "fails when with expect_any_instance_of is used and the entire chain is not called" do - o = Object.new - expect { expect_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) verify_all From 6c8067899fd12466ddfe58439b677c3a7bb624d4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 19 Nov 2013 22:54:49 -0800 Subject: [PATCH 0335/1179] Add changelog entry. --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 9246368fe..6461afe71 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,9 @@ Enhancements: * Add receive_message_chain which provides the functionality of the old stub_chain for the new allow/expect syntax. Use it like so: allow(...).to receive_message_chain(:foo, :bar, :bazz). (Sam Phippen). +* Change argument matchers to use `===` as their primary matching + protocol, as their semantics mirror that of a case or rescue statement + (which uses `===` for matching). (Myron Marston) ### 3.0.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) From cd81094af90793fac04325f6ce6585169581e605 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 21 Nov 2013 23:26:26 -0800 Subject: [PATCH 0336/1179] Refactor TestDouble#method_missing. * Always record the message was received. I think it was a bug that for `to_int`, `to_a` or `to_ary` that was skipped. * Move the `return self` case for null objects into the existing case statement: no reason to have it later. * Memoize `__mock_proxy` in a local -- this saves us from having to look it up in a hash a few times. * Simplify the way errors get raised from this method. Rather than calling `super` and rescuing `NameError`, Just raise the errors directly. I believe this will be faster but haven't benchmarked it. --- lib/rspec/mocks/test_double.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index a095a380b..f0fc7fc38 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -78,21 +78,20 @@ def __initialize_as_test_double(name=nil, stubs={}) end def method_missing(message, *args, &block) - if __mock_proxy.null_object? + proxy = __mock_proxy + proxy.record_message_received(message, *args, &block) + + if proxy.null_object? case message when :to_int then return 0 when :to_a, :to_ary then return nil + else return self end end - __mock_proxy.record_message_received(message, *args, &block) - - begin - __mock_proxy.null_object? ? self : super - rescue NameError - # Required wrapping doubles in an Array on Ruby 1.9.2 - raise NoMethodError if [:to_a, :to_ary].include? message - __mock_proxy.raise_unexpected_message_error(message, *args) - end + + # Required wrapping doubles in an Array on Ruby 1.9.2 + raise NoMethodError if [:to_a, :to_ary].include? message + proxy.raise_unexpected_message_error(message, *args) end def assign_stubs(stubs) From c720de3063b1186de607dc8bdef1ff011f9c5e32 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 21 Nov 2013 14:06:21 -0800 Subject: [PATCH 0337/1179] Use symbols not strings for visibility. --- lib/rspec/mocks/method_double.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 6ec7b5c6a..077bf5d0a 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -36,13 +36,13 @@ def original_method # @private def visibility if TestDouble === @object - 'public' + :public elsif object_singleton_class.private_method_defined?(@method_name) - 'private' + :private elsif object_singleton_class.protected_method_defined?(@method_name) - 'protected' + :protected else - 'public' + :public end end From 0e9f28b202daa7f85e6c76633f917eccbfaf557a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 21 Nov 2013 14:13:57 -0800 Subject: [PATCH 0338/1179] Refactor: favor polymorphism over type checks. --- lib/rspec/mocks/method_double.rb | 10 +--------- lib/rspec/mocks/proxy.rb | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 077bf5d0a..6452bb75a 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -35,15 +35,7 @@ def original_method # @private def visibility - if TestDouble === @object - :public - elsif object_singleton_class.private_method_defined?(@method_name) - :private - elsif object_singleton_class.protected_method_defined?(@method_name) - :protected - else - :public - end + @proxy.visibility_for(@method_name) end # @private diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 232d5ff93..40a9fb685 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -192,6 +192,12 @@ def raise_missing_default_stub_error(expectation, *args) @error_generator.raise_missing_default_stub_error(expectation, *args) end + # @private + def visibility_for(method_name) + # This is the default (for test doubles). Subclasses override this. + :public + end + private def method_double_for(message) @@ -257,6 +263,19 @@ def add_simple_stub(method_name, response) super end + # @private + def visibility_for(method_name) + klass = class << @object; self; end + + if klass.private_method_defined?(method_name) + :private + elsif klass.protected_method_defined?(method_name) + :protected + else + :public + end + end + private def any_instance_class_recorder_observing_method?(klass, method_name) From 1f7aa214d948c3475e7bfec0719ad0e38cf1b47c Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sat, 23 Nov 2013 00:12:28 +0000 Subject: [PATCH 0339/1179] Only disable the should syntax warnings when the default syntax host is passed --- lib/rspec/mocks/syntax.rb | 2 +- spec/rspec/mocks/syntax_spec.rb | 19 +++++++++++++++++++ spec/spec_helper.rb | 13 +++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 spec/rspec/mocks/syntax_spec.rb diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 4a75a7b0e..3cd1168c4 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -24,7 +24,7 @@ def self.warn_unless_should_configured(method_name) # @api private # Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc). def self.enable_should(syntax_host = default_should_syntax_host) - @warn_about_should = false + @warn_about_should = false if syntax_host == default_should_syntax_host return if should_enabled?(syntax_host) syntax_host.class_exec do diff --git a/spec/rspec/mocks/syntax_spec.rb b/spec/rspec/mocks/syntax_spec.rb new file mode 100644 index 000000000..3e2bd3b9f --- /dev/null +++ b/spec/rspec/mocks/syntax_spec.rb @@ -0,0 +1,19 @@ +require "spec_helper" + +module RSpec::Mocks + describe Syntax do + context "when the should syntax is enabled on a non-default syntax host" do + include_context "with the default mocks syntax" + + it "continues to warn about the should syntax" do + my_host = Class.new + expect(RSpec).to receive(:deprecate) + Syntax.enable_should(my_host) + + o = Object.new + o.should_receive(:bees) + o.bees + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9a40070db..49c88e189 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -111,3 +111,16 @@ def prevents(&block) end end +shared_context "with the default mocks syntax" do + orig_syntax = nil + + before(:all) do + orig_syntax = RSpec::Mocks.configuration.syntax + RSpec::Mocks.configuration.reset_syntaxes_to_default + end + + after(:all) do + RSpec::Mocks.configuration.syntax = orig_syntax + end + +end From 4cd26ac3bd844441d19bc8e5e5b5efe09234a5f3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 21 Nov 2013 14:30:34 -0800 Subject: [PATCH 0340/1179] Verified doubles should match the visibility of the interface they verify against. --- lib/rspec/mocks/method_reference.rb | 39 +++++++ lib/rspec/mocks/proxy.rb | 10 +- lib/rspec/mocks/test_double.rb | 6 ++ lib/rspec/mocks/verifying_proxy.rb | 4 + spec/rspec/mocks/verifying_double_spec.rb | 125 +++++++++++++++++++--- 5 files changed, 163 insertions(+), 21 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index e6a6c9f54..4a5b0d702 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -37,12 +37,33 @@ def when_unimplemented yield unless implemented? end + def visibility + @module_reference.when_loaded do |m| + return visibility_from(m) + end + + # When it's not loaded, assume it's public. We don't want to + # wrongly treat the method as private. + :public + end + private + def original_method @module_reference.when_loaded do |m| self.defined? && find_method(m) end end + + def self.visibility_for(klass, method_name) + if klass.private_method_defined?(method_name) + :private + elsif klass.protected_method_defined?(method_name) + :protected + elsif klass.method_defined?(method_name) + :public + end + end end # @private @@ -74,6 +95,10 @@ def find_method(m) m.instance_method(@method_name) end end + + def visibility_from(m) + MethodReference.visibility_for(m, @method_name) + end end # @private @@ -90,6 +115,20 @@ def method_defined?(m) def find_method(m) m.method(@method_name) end + + def visibility_from(m) + MethodReference.visibility_for(class << m; self; end, @method_name).tap do |vis| + # If the method is not defined on the class, `visibility_for` returns `nil`. + # However, it may be handled dynamically by `method_missing`, so here we + # check `respond_to` (passing false to not check private methods). + # + # This only considers the :public case, but I don't think it's possible to + # write `method_missing` in such a way that it handles a dynamic message + # with private or protected visibility. Ruby doesn't provide you with + # the caller info. + return :public if vis.nil? && m.respond_to?(@method_name, false) + end + end end end end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 40a9fb685..c4ddace2d 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -265,15 +265,7 @@ def add_simple_stub(method_name, response) # @private def visibility_for(method_name) - klass = class << @object; self; end - - if klass.private_method_defined?(method_name) - :private - elsif klass.protected_method_defined?(method_name) - :protected - else - :public - end + MethodReference.visibility_for(class << @object; self; end, method_name) || :public end private diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index f0fc7fc38..5275bba66 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -81,6 +81,12 @@ def method_missing(message, *args, &block) proxy = __mock_proxy proxy.record_message_received(message, *args, &block) + # Defined private and protected methods will still trigger `method_missing` + # when called publicly. We want ruby's method visibility error to get raised, + # so we simply delegate to `super` in that case. + visibility = proxy.visibility_for(message) + return super if visibility == :private || visibility == :protected + if proxy.null_object? case message when :to_int then return 0 diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index af105c097..e40b6b193 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -69,6 +69,10 @@ def method_reference h[k] = @method_reference_class.new(@doubled_module, k) end end + + def visibility_for(method_name) + method_reference[method_name].visibility + end end # @api private diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 0b1af0692..0ac37a2cb 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -19,6 +19,11 @@ def send # fake out! end + protected + + def defined_protected_class_method + end + private def defined_private_class_method @@ -36,6 +41,11 @@ def respond_to?(method_name, include_all = false) class Nested; end +protected + + def defined_protected_method + end + private def defined_private_method @@ -98,14 +108,48 @@ module Mocks prevents { o.should_receive(:defined_class_method) } end - it 'allows instance methods that are private' do - o = instance_double('LoadedClass') - allow(o).to receive(:defined_private_method) - o.send :defined_private_method - - o2 = instance_double('LoadedClass') - expect(o2).to receive(:defined_private_method) - o2.send :defined_private_method + describe "method visibility" do + shared_examples_for "preserves method visibility" do |visibility| + method_name = :"defined_#{visibility}_method" + + it "can allow a #{visibility} instance method" do + o = instance_double('LoadedClass') + allow(o).to receive(method_name).and_return(3) + expect(o.send method_name).to eq(3) + end + + it "can expect a #{visibility} instance method" do + o = instance_double('LoadedClass') + expect(o).to receive(method_name) + o.send method_name + end + + it "preserves #{visibility} visibility when allowing a #{visibility} method" do + preserves_visibility(method_name, visibility) do + instance_double('LoadedClass').tap do |o| + allow(o).to receive(method_name) + end + end + end + + it "preserves #{visibility} visibility when expecting a #{visibility} method" do + preserves_visibility(method_name, visibility) do + instance_double('LoadedClass').tap do |o| + expect(o).to receive(method_name) + o.send(method_name) # to satisfy the expectation + end + end + end + + it "preserves #{visibility} visibility on a null object" do + preserves_visibility(method_name, visibility) do + instance_double('LoadedClass').as_null_object + end + end + end + + include_examples "preserves method visibility", :private + include_examples "preserves method visibility", :protected end it 'does not allow dynamic methods to be expected' do @@ -184,9 +228,8 @@ module Mocks end it 'only allows class methods that exist to be stubbed' do - o = class_double('LoadedClass', :defined_class_method => 1, :defined_private_class_method => 42) + o = class_double('LoadedClass', :defined_class_method => 1) expect(o.defined_class_method).to eq(1) - expect(o.send :defined_private_class_method).to eq(42) prevents { o.stub(:undefined_instance_method) } prevents { o.stub(:defined_instance_method) } @@ -195,9 +238,7 @@ module Mocks it 'only allows class methods that exist to be expected' do o = class_double('LoadedClass') expect(o).to receive(:defined_class_method) - expect(o).to receive(:defined_private_class_method) o.defined_class_method - o.send :defined_private_class_method prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_instance_method) } @@ -205,6 +246,50 @@ module Mocks prevents { o.should_receive(:defined_instance_method) } end + describe "method visibility" do + shared_examples_for "preserves method visibility" do |visibility| + method_name = :"defined_#{visibility}_class_method" + + it "can allow a #{visibility} instance method" do + o = class_double('LoadedClass') + allow(o).to receive(method_name).and_return(3) + expect(o.send method_name).to eq(3) + end + + it "can expect a #{visibility} instance method" do + o = class_double('LoadedClass') + expect(o).to receive(method_name) + o.send method_name + end + + it "preserves #{visibility} visibility when allowing a #{visibility} method" do + preserves_visibility(method_name, visibility) do + class_double('LoadedClass').tap do |o| + allow(o).to receive(method_name) + end + end + end + + it "preserves #{visibility} visibility when expecting a #{visibility} method" do + preserves_visibility(method_name, visibility) do + class_double('LoadedClass').tap do |o| + expect(o).to receive(method_name) + o.send(method_name) # to satisfy the expectation + end + end + end + + it "preserves #{visibility} visibility on a null object" do + preserves_visibility(method_name, visibility) do + class_double('LoadedClass').as_null_object + end + end + end + + include_examples "preserves method visibility", :private + include_examples "preserves method visibility", :protected + end + it 'checks that stubbed methods are invoked with the correct arity' do o = class_double('LoadedClass', :defined_class_method => 1) expect { @@ -358,6 +443,22 @@ module Mocks expect { instance_double(1) }.to raise_error(ArgumentError) expect { instance_double(nil) }.to raise_error(ArgumentError) end + + def preserves_visibility(method_name, visibility) + double = yield + + expect { + # send bypasses visbility, so we use eval instead. + eval("double.#{method_name}") + }.to raise_error(NoMethodError, /#{visibility}/) + + unless double.null_object? + # Null object doubles use `method_missing` and so the singleton class + # doesn't know what methods are defined. + singleton_class = class << double; self; end + expect(singleton_class.send("#{visibility}_method_defined?", method_name)).to be true + end + end end end end From bec8f147e2d526e7fe47bf331932d2757a613e71 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Nov 2013 16:26:49 -0800 Subject: [PATCH 0341/1179] Verified null object doubles should implement `respond_to?` correctly. --- lib/rspec/mocks/verifying_double.rb | 8 ++++++++ spec/rspec/mocks/verifying_double_spec.rb | 3 +++ 2 files changed, 11 insertions(+) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index a511656e6..478e1ebbe 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -5,6 +5,14 @@ module Mocks # @api private module VerifyingDouble + def respond_to?(message, include_private=false) + if null_object? + __mock_proxy.method_reference[message].when_unimplemented { return false } + end + + super + end + def method_missing(message, *args, &block) # Null object conditional is an optimization. If not a null object, # validity of method expectations will have been checked at definition diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 0ac37a2cb..217d5d77a 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -188,7 +188,10 @@ module Mocks o = instance_double('LoadedClass').as_null_object expect(o.defined_instance_method).to eq(o) + expect(o).to respond_to(:defined_instance_method) + prevents { o.undefined_method } + expect(o).not_to respond_to(:undefined_method) end end From 4187a5a8c20e0377ed5022beae866016b5f0701c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Nov 2013 16:31:17 -0800 Subject: [PATCH 0342/1179] Refactor method_visibility helper methods. --- lib/rspec/mocks/method_reference.rb | 30 ++++++++++++++++------------- lib/rspec/mocks/proxy.rb | 4 +++- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 4a5b0d702..26221dc56 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -55,7 +55,7 @@ def original_method end end - def self.visibility_for(klass, method_name) + def self.instance_method_visibility_for(klass, method_name) if klass.private_method_defined?(method_name) :private elsif klass.protected_method_defined?(method_name) @@ -64,6 +64,20 @@ def self.visibility_for(klass, method_name) :public end end + + def self.method_visibility_for(object, method_name) + instance_method_visibility_for(class << object; self; end, method_name).tap do |vis| + # If the method is not defined on the class, `instance_method_visibility_for` + # returns `nil`. However, it may be handled dynamically by `method_missing`, + # so here we check `respond_to` (passing false to not check private methods). + # + # This only considers the public case, but I don't think it's possible to + # write `method_missing` in such a way that it handles a dynamic message + # with private or protected visibility. Ruby doesn't provide you with + # the caller info. + return :public if vis.nil? && object.respond_to?(method_name, false) + end + end end # @private @@ -97,7 +111,7 @@ def find_method(m) end def visibility_from(m) - MethodReference.visibility_for(m, @method_name) + MethodReference.instance_method_visibility_for(m, @method_name) end end @@ -117,17 +131,7 @@ def find_method(m) end def visibility_from(m) - MethodReference.visibility_for(class << m; self; end, @method_name).tap do |vis| - # If the method is not defined on the class, `visibility_for` returns `nil`. - # However, it may be handled dynamically by `method_missing`, so here we - # check `respond_to` (passing false to not check private methods). - # - # This only considers the :public case, but I don't think it's possible to - # write `method_missing` in such a way that it handles a dynamic message - # with private or protected visibility. Ruby doesn't provide you with - # the caller info. - return :public if vis.nil? && m.respond_to?(@method_name, false) - end + MethodReference.method_visibility_for(m, @method_name) end end end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index c4ddace2d..577b055e8 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -265,7 +265,9 @@ def add_simple_stub(method_name, response) # @private def visibility_for(method_name) - MethodReference.visibility_for(class << @object; self; end, method_name) || :public + # We fall back to :public because by default we allow undefined methods + # to be stubbed, and when we do so, we make them public. + MethodReference.method_visibility_for(@object, method_name) || :public end private From 0204ce28203ad52e3018f1ec624dc957179ca37c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Nov 2013 16:32:53 -0800 Subject: [PATCH 0343/1179] `m` is a terrible variable name. --- lib/rspec/mocks/method_reference.rb | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 26221dc56..7b7640713 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -83,8 +83,8 @@ def self.method_visibility_for(object, method_name) # @private class InstanceMethodReference < MethodReference private - def method_implemented?(m) - m.method_defined?(@method_name) || m.private_method_defined?(@method_name) + def method_implemented?(mod) + mod.method_defined?(@method_name) || mod.private_method_defined?(@method_name) end # Ideally, we'd use `respond_to?` for `method_implemented?` but we need a @@ -101,37 +101,37 @@ def method_implemented?(m) # This is necessary due to a bug in JRuby prior to 1.7.5 fixed in: # https://github.com/jruby/jruby/commit/99a0613fe29935150d76a9a1ee4cf2b4f63f4a27 if RUBY_PLATFORM == 'java' && JRUBY_VERSION.split('.')[-1].to_i < 5 - def find_method(m) - m.dup.instance_method(@method_name) + def find_method(mod) + mod.dup.instance_method(@method_name) end else - def find_method(m) - m.instance_method(@method_name) + def find_method(mod) + mod.instance_method(@method_name) end end - def visibility_from(m) - MethodReference.instance_method_visibility_for(m, @method_name) + def visibility_from(mod) + MethodReference.instance_method_visibility_for(mod, @method_name) end end # @private class ObjectMethodReference < MethodReference private - def method_implemented?(m) - m.respond_to?(@method_name, true) + def method_implemented?(object) + object.respond_to?(@method_name, true) end - def method_defined?(m) - (class << m; self; end).method_defined?(@method_name) + def method_defined?(object) + (class << object; self; end).method_defined?(@method_name) end - def find_method(m) - m.method(@method_name) + def find_method(object) + object.method(@method_name) end - def visibility_from(m) - MethodReference.method_visibility_for(m, @method_name) + def visibility_from(object) + MethodReference.method_visibility_for(object, @method_name) end end end From ef08a22804ac84bcc43d73bdda2c2ed6a8635e74 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Nov 2013 16:34:13 -0800 Subject: [PATCH 0344/1179] Rename mis-named variable. It may not be a module reference anymore. It may be an object reference. --- lib/rspec/mocks/method_reference.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 7b7640713..eea9d3ba4 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -1,11 +1,13 @@ module RSpec module Mocks - # Represents a method on a module that may or may not be defined. + # Represents a method on an object that may or may not be defined. + # The method may be an instance method on a module or a method on + # any object. # # @private class MethodReference - def initialize(module_reference, method_name) - @module_reference = module_reference + def initialize(object_reference, method_name) + @object_reference = object_reference @method_name = method_name end @@ -13,7 +15,7 @@ def initialize(module_reference, method_name) # a `NoMethodError`. It might be dynamically implemented by # `method_missing`. def implemented? - @module_reference.when_loaded do |m| + @object_reference.when_loaded do |m| method_implemented?(m) end end @@ -21,7 +23,7 @@ def implemented? # A method is defined if we are able to get a `Method` object for it. # In that case, we can assert against metadata like the arity. def defined? - @module_reference.when_loaded do |m| + @object_reference.when_loaded do |m| method_defined?(m) end end @@ -38,7 +40,7 @@ def when_unimplemented end def visibility - @module_reference.when_loaded do |m| + @object_reference.when_loaded do |m| return visibility_from(m) end @@ -50,7 +52,7 @@ def visibility private def original_method - @module_reference.when_loaded do |m| + @object_reference.when_loaded do |m| self.defined? && find_method(m) end end From 7c1fd12626fc53f657c8bac3b787bca6c86d5975 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Nov 2013 17:28:34 -0800 Subject: [PATCH 0345/1179] Use `public_method_defined?` not `method_defined?`. It's more accurate. Also put the more common case (public methods) first. It should be hit more often and thus be a tiny bit faster. --- lib/rspec/mocks/method_reference.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index eea9d3ba4..85e7bd3d1 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -58,12 +58,12 @@ def original_method end def self.instance_method_visibility_for(klass, method_name) - if klass.private_method_defined?(method_name) + if klass.public_method_defined?(method_name) + :public + elsif klass.private_method_defined?(method_name) :private elsif klass.protected_method_defined?(method_name) :protected - elsif klass.method_defined?(method_name) - :public end end From cfca5705778f770df8f0f3fb92ae0b80fe7c7c10 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Nov 2013 17:34:46 -0800 Subject: [PATCH 0346/1179] Leverage our new method visibility helper methods. Fixes #475. --- lib/rspec/mocks/any_instance/recorder.rb | 2 +- lib/rspec/mocks/instance_method_stasher.rb | 2 +- lib/rspec/mocks/method_double.rb | 3 +-- lib/rspec/mocks/method_reference.rb | 6 +++++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 1a1b7eb20..cd188d9b8 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -172,7 +172,7 @@ def backup_method!(method_name) end def public_protected_or_private_method_defined?(method_name) - @klass.method_defined?(method_name) || @klass.private_method_defined?(method_name) + MethodReference.method_defined_at_any_visibility?(@klass, method_name) end def stop_observing!(method_name) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index 58d5cb9e6..ef295fa81 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -79,7 +79,7 @@ def method_defined_directly_on_klass? # @private def method_defined_on_klass?(klass = @klass) - klass.method_defined?(@method) || klass.private_method_defined?(@method) + MethodReference.method_defined_at_any_visibility?(klass, @method) end def method_owned_by_klass? diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 6452bb75a..4a08945bf 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -90,8 +90,7 @@ def restore_original_method # @private def restore_original_visibility return unless @original_visibility && - (object_singleton_class.method_defined?(@method_name) || - object_singleton_class.private_method_defined?(@method_name)) + MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name) object_singleton_class.__send__(*@original_visibility) end diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 85e7bd3d1..e3e89ba0a 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -67,6 +67,10 @@ def self.instance_method_visibility_for(klass, method_name) end end + class << self + alias method_defined_at_any_visibility? instance_method_visibility_for + end + def self.method_visibility_for(object, method_name) instance_method_visibility_for(class << object; self; end, method_name).tap do |vis| # If the method is not defined on the class, `instance_method_visibility_for` @@ -86,7 +90,7 @@ def self.method_visibility_for(object, method_name) class InstanceMethodReference < MethodReference private def method_implemented?(mod) - mod.method_defined?(@method_name) || mod.private_method_defined?(@method_name) + MethodReference.method_defined_at_any_visibility?(mod, @method_name) end # Ideally, we'd use `respond_to?` for `method_implemented?` but we need a From 6bd92e583cdb320cf98c1638114b69857ad0dd22 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Nov 2013 17:37:20 -0800 Subject: [PATCH 0347/1179] Add changelog entries. --- Changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.md b/Changelog.md index 2bb5cd663..d3583428a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,12 @@ Bug Fixes: define a `let` with the same name as one of the methods from `RSpec::Mocks::ArgumentMatchers`, the user's `let` takes precedence. (Michi Huber, Myron Marston) +* Fix verified doubles so that their methods match the visibility + (public, protected or private) of the interface they verify + against. (Myron Marston) +* Fix verified null object doubles so that they do not wrongly + report that they respond to anything. They only respond to methods + available on the interface they verify against. (Myron Marston) Enhancements: From de41eeaf801e27e67748b33db2dc716669af89e9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Nov 2013 20:56:24 -0800 Subject: [PATCH 0348/1179] Fix null_object `respond_to?` to handle private/protected correctly. --- lib/rspec/mocks/method_reference.rb | 20 ++++++--- lib/rspec/mocks/verifying_double.rb | 13 ++++-- lib/rspec/mocks/verifying_proxy.rb | 8 ++-- spec/rspec/mocks/verifying_double_spec.rb | 55 ++++++++++++++++++++--- 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index e3e89ba0a..a1e18074f 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -20,6 +20,21 @@ def implemented? end end + # Returns true if we definitively know that sending the method + # will result in a `NoMethodError`. + # + # This is not simply the inverse of `implemented?`: there are + # cases when we don't know if a method is implemented and + # both `implemented?` and `unimplemented?` will return false. + def unimplemented? + @object_reference.when_loaded do |m| + return !implemented? + end + + # If it's not loaded, then it may be implemented but we can't check. + false + end + # A method is defined if we are able to get a `Method` object for it. # In that case, we can assert against metadata like the arity. def defined? @@ -34,11 +49,6 @@ def when_defined end end - # Yields to the block if the method is not implemented. - def when_unimplemented - yield unless implemented? - end - def visibility @object_reference.when_loaded do |m| return visibility_from(m) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 478e1ebbe..8d1a8bdbb 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -6,11 +6,16 @@ module Mocks # @api private module VerifyingDouble def respond_to?(message, include_private=false) - if null_object? - __mock_proxy.method_reference[message].when_unimplemented { return false } - end + return super unless null_object? - super + method_ref = __mock_proxy.method_reference[message] + + return case method_ref.visibility + when :public then true + when :private then include_private + when :protected then include_private || RUBY_VERSION.to_f < 2.0 + else !method_ref.unimplemented? + end end def method_missing(message, *args, &block) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index e40b6b193..254eb67cc 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -22,9 +22,7 @@ def add_message_expectation(location, method_name, opts={}, &block) end def ensure_implemented(method_name) - return unless @doubled_module.defined? - - method_reference[method_name].when_unimplemented do + if method_reference[method_name].unimplemented? @error_generator.raise_unimplemented_error( @doubled_module, method_name @@ -149,8 +147,8 @@ def when_defined yield original_method end - def when_unimplemented - yield unless @valid_method + def unimplemented? + !@valid_method end end end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 217d5d77a..10cd05108 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -80,6 +80,20 @@ module Mocks expect(o.undefined_instance_method(:arg)).to eq(true) end + + context 'for null objects' do + let(:o) { instance_double('NonLoadedClass').as_null_object } + + it 'returns self from any message' do + expect(o.a.b.c).to be(o) + end + + it 'reports it responds to any message' do + expect(o.respond_to?(:a)).to be true + expect(o.respond_to?(:a, false)).to be true + expect(o.respond_to?(:a, true)).to be true + end + end end describe 'when doubled class is loaded' do @@ -184,14 +198,43 @@ module Mocks expect(o.defined_instance_method).to eq(1) end - it 'only allows defined methods for null objects' do - o = instance_double('LoadedClass').as_null_object + context 'for null objects' do + let(:o) { instance_double('LoadedClass').as_null_object } - expect(o.defined_instance_method).to eq(o) - expect(o).to respond_to(:defined_instance_method) + it 'only allows defined methods' do + expect(o.defined_instance_method).to eq(o) + prevents { o.undefined_method } + end - prevents { o.undefined_method } - expect(o).not_to respond_to(:undefined_method) + it 'reports what public messages it responds to accurately' do + expect(o.respond_to?(:defined_instance_method)).to be true + expect(o.respond_to?(:defined_instance_method, true)).to be true + expect(o.respond_to?(:defined_instance_method, false)).to be true + + expect(o.respond_to?(:undefined_method)).to be false + expect(o.respond_to?(:undefined_method, true)).to be false + expect(o.respond_to?(:undefined_method, false)).to be false + end + + it 'reports that it responds to defined private methods when the appropriate arg is passed' do + expect(o.respond_to?(:defined_private_method)).to be false + expect(o.respond_to?(:defined_private_method, true)).to be true + expect(o.respond_to?(:defined_private_method, false)).to be false + end + + if RUBY_VERSION.to_f < 2.0 # respond_to?(:protected_method) changed behavior in Ruby 2.0. + it 'reports that it responds to protected methods' do + expect(o.respond_to?(:defined_protected_method)).to be true + expect(o.respond_to?(:defined_protected_method, true)).to be true + expect(o.respond_to?(:defined_protected_method, false)).to be true + end + else + it 'reports that it responds to protected methods when the appropriate arg is passed' do + expect(o.respond_to?(:defined_protected_method)).to be false + expect(o.respond_to?(:defined_protected_method, true)).to be true + expect(o.respond_to?(:defined_protected_method, false)).to be false + end + end end end From 6e86448e6a15f71607a889ff1d66e882fdf01963 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 23 Nov 2013 10:13:58 -0800 Subject: [PATCH 0349/1179] Add benchmark. --- .../method_defined_at_any_visibility.rb | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 benchmarks/method_defined_at_any_visibility.rb diff --git a/benchmarks/method_defined_at_any_visibility.rb b/benchmarks/method_defined_at_any_visibility.rb new file mode 100644 index 000000000..d538c2076 --- /dev/null +++ b/benchmarks/method_defined_at_any_visibility.rb @@ -0,0 +1,101 @@ +require 'benchmark' + +n = 1_000_000 + +Foo = Class.new do + 1.upto(n) do |i| + define_method(:"public_method_#{i}") { } + define_method(:"protected_method_#{i}") { } + protected :"protected_method_#{i}" + define_method(:"private_method_#{i}") { } + private :"protected_method_#{i}" + end +end + +Benchmark.benchmark do |bm| + puts "#{n} times - ruby #{RUBY_VERSION}" + + puts + puts "using method_defined? and private_method_defined?" + puts + + [:public, :protected, :private, :undefined].each do |vis| + puts " - #{vis} methods" + + 3.times do + GC.start + + bm.report do + n.times do |i| + name = :"#{vis}_method_#{i}" + Foo.method_defined?(name) || Foo.private_method_defined?(name) + end + end + end + end + + puts + puts "using public_method_defined?, protected_method_defined? and private_method_defined?" + puts + + [:public, :protected, :private, :undefined].each do |vis| + puts " - #{vis} methods" + + 3.times do + GC.start + + bm.report do + n.times do |i| + name = :"#{vis}_method_#{i}" + Foo.public_method_defined?(name) || + Foo.protected_method_defined?(name) + Foo.private_method_defined?(name) + end + end + end + end +end + +=begin + +1000000 times - ruby 2.0.0 + +using method_defined? and private_method_defined? + + - public methods + 1.410000 0.040000 1.450000 ( 1.462588) + 1.380000 0.000000 1.380000 ( 1.372015) + 1.370000 0.000000 1.370000 ( 1.372362) + - protected methods + 1.410000 0.000000 1.410000 ( 1.402750) + 1.440000 0.000000 1.440000 ( 1.442719) + 1.460000 0.010000 1.470000 ( 1.464763) + - private methods + 1.390000 0.000000 1.390000 ( 1.393956) + 1.340000 0.000000 1.340000 ( 1.349340) + 1.360000 0.000000 1.360000 ( 1.361910) + - undefined methods + 3.260000 0.050000 3.310000 ( 3.316372) + 1.260000 0.010000 1.270000 ( 1.266557) + 1.250000 0.000000 1.250000 ( 1.248734) + +using public_method_defined?, protected_method_defined? and private_method_defined? + + - public methods + 1.550000 0.000000 1.550000 ( 1.550655) + 1.540000 0.010000 1.550000 ( 1.543906) + 1.540000 0.000000 1.540000 ( 1.538267) + - protected methods + 1.590000 0.000000 1.590000 ( 1.598310) + 1.600000 0.000000 1.600000 ( 1.595205) + 1.600000 0.000000 1.600000 ( 1.604186) + - private methods + 1.530000 0.000000 1.530000 ( 1.530080) + 1.560000 0.000000 1.560000 ( 1.562656) + 1.560000 0.000000 1.560000 ( 1.569161) + - undefined methods + 1.300000 0.000000 1.300000 ( 1.298066) + 1.310000 0.000000 1.310000 ( 1.310737) + 1.290000 0.000000 1.290000 ( 1.288307) + +=end From c442be72cdec551a6f36d9e77b2f191576d690db Mon Sep 17 00:00:00 2001 From: sanemat Date: Sun, 24 Nov 2013 15:02:13 +0900 Subject: [PATCH 0350/1179] Test against ruby 2.1.0-preview2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9feb1ed0a..8a2931a5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ rvm: - rbx-18mode - rbx-19mode - 2.0.0 - - 2.1.0-preview1 + - 2.1.0-preview2 matrix: allow_failures: - rvm: rbx-18mode From 4a8f672b33ad74c41e7a7b7f8054a17db80543c0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 25 Nov 2013 08:38:37 -0800 Subject: [PATCH 0351/1179] Fix expect syntax to properly forward do..end blocks. Previously, `do...end` blocks were forwarded to `MessageExpectation#initialize`, such that something like this: allow(obj).to receive(:foo).with(1) do :return_value end ...was translated into: obj.stub(:foo) { :return_value }.with(1) In practice, this caused a confusing/wrong "you are overriding this stub" warning. Our fix is to forward them on to the last method in the matcher chain. This ensures that it gets forwarded to the method the user intends. --- lib/rspec/mocks/matchers/receive.rb | 12 ++++++++++++ spec/rspec/mocks/matchers/receive_spec.rb | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 1b5af2d98..cf565e601 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -86,6 +86,8 @@ def setup_any_instance_method_substitute(subject, method, block) def setup_method_substitute(host, method, block, *args) args << @message.to_sym + block = move_block_to_last_customization(block) + expectation = host.__send__(method, *args, &(@block || block)) @recorded_customizations.each do |customization| @@ -93,10 +95,20 @@ def setup_method_substitute(host, method, block, *args) end expectation end + + def move_block_to_last_customization(block) + last = @recorded_customizations.last + return block unless last + + last.block ||= block + nil + end end end class ExpectationCustomization + attr_accessor :block + def initialize(method_name, args, block) @method_name = method_name @args = args diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 8b077922d..574d57ebc 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -93,6 +93,24 @@ def verify_all expect(receiver.foo(1)).to eq(:a) expect(receiver.foo(2)).to eq(:b) end + + it 'allows do...end blocks to be passed to the fluent interface methods without getting a warning' do + expect(RSpec).not_to receive(:warning) + + wrapped.to receive(:foo).with(1) do + :a + end + + expect(receiver.foo(1)).to eq(:a) + end + + it 'makes { } blocks trump do...end blocks when passed to a fluent interface method' do + wrapped.to receive(:foo).with(1) { :curly } do + :do_end + end + + expect(receiver.foo(1)).to eq(:curly) + end end shared_examples_for "an expect syntax allowance" do |*options| From 1d74976393c4f8cae913babd0e7625e433a92c17 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Nov 2013 09:21:04 -0800 Subject: [PATCH 0352/1179] Fix warning to only be emitted when it is true. --- lib/rspec/mocks/message_expectation.rb | 9 +++++++-- .../mocks/combining_implementation_instructions_spec.rb | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 46a376685..6139a5e6b 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -524,8 +524,13 @@ def initial_implementation_action=(action) end def inner_implementation_action=(action) - RSpec.warning("You're overriding a previous implementation for this stub") if implementation.inner_action - implementation.inner_action = action if action + return unless action + + if implementation.inner_action + RSpec.warning("You're overriding a previous implementation for this stub") + end + + implementation.inner_action = action end def terminal_implementation_action=(action) diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 8a9de33a5..c793212ed 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -175,6 +175,14 @@ def verify_combined_implementation double.stub(:foo).with(:arg) { :with_block }.at_least(:once) { :at_least_block } end + it "does not warn about overriding the stub when `:with` is chained off the block" do + expect(RSpec).not_to receive(:warning) + + obj = Object.new + stub = obj.stub(:foo) { } + stub.with(1) + end + it 'can combine and_call_original, with, and_return' do obj = Struct.new(:value).new('original') obj.stub(:value).and_call_original From f7035d6333ece7b26508e73237c6323a1eb5469e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Nov 2013 09:37:25 -0800 Subject: [PATCH 0353/1179] Improve stub override warning message. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Mention what method's stub is being overridden. - Include the call site. The old message was: You're overriding a previous implementation for this stub. …which doesn't give you much to go off of. --- lib/rspec/mocks/message_expectation.rb | 17 +++++++++-------- spec/rspec/mocks/and_call_original_spec.rb | 2 +- ...ombining_implementation_instructions_spec.rb | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 6139a5e6b..17e699b4e 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -151,9 +151,7 @@ def and_call_original if RSpec::Mocks::TestDouble === @method_double.object @error_generator.raise_only_valid_on_a_partial_double(:and_call_original) else - if implementation.inner_action - RSpec.warning("You're overriding a previous implementation for this stub") - end + warn_about_stub_override if implementation.inner_action @implementation = AndCallOriginalImplementation.new(@method_double.original_method) @yield_receiver_to_implementation_block = false end @@ -525,17 +523,20 @@ def initial_implementation_action=(action) def inner_implementation_action=(action) return unless action - - if implementation.inner_action - RSpec.warning("You're overriding a previous implementation for this stub") - end - + warn_about_stub_override if implementation.inner_action implementation.inner_action = action end def terminal_implementation_action=(action) implementation.terminal_action = action end + + def warn_about_stub_override + RSpec.warning( + "You're overriding a previous stub implementation of `#{@message}`. " + + "Called from #{CallerFilter.first_non_rspec_line}." + ) + end end # Handles the implementation of an `and_yield` declaration. diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index a1524df5a..7279f61af 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -47,7 +47,7 @@ def self.new_instance end it 'warns when you override an existing implementation' do - expect(RSpec).to receive(:warning).with(/overriding a previous implementation/) + expect(RSpec).to receive(:warning).with(/overriding a previous stub implementation of `meth_1`.*#{__FILE__}:#{__LINE__ + 1}/) expect(instance).to receive(:meth_1) { true }.and_call_original instance.meth_1 end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index c793212ed..ca2a34f97 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -171,7 +171,7 @@ def verify_combined_implementation end it 'warns when the inner implementation block is overriden' do - expect(RSpec).to receive(:warning).with(/overriding a previous implementation/) + expect(RSpec).to receive(:warning).with(/overriding a previous stub implementation of `foo`.*#{__FILE__}:#{__LINE__ + 1}/) double.stub(:foo).with(:arg) { :with_block }.at_least(:once) { :at_least_block } end From e60dd3d56e677aa8287b794db64d1c63d165f4b3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Nov 2013 09:40:20 -0800 Subject: [PATCH 0354/1179] Remove old invalid comment. --- lib/rspec/mocks/matchers/receive.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index cf565e601..787ef6c25 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -6,12 +6,7 @@ def initialize(message, block) @message = message @block = block @recorded_customizations = [] - - # MRI, JRuby and RBX report the caller inconsistently; MRI - # reports an extra "in `new'" line in the backtrace that the - # others do not include. The safest way to find the right - # line is to search for the first line BEFORE rspec/mocks/syntax.rb. - @backtrace_line = CallerFilter.first_non_rspec_line + @backtrace_line = CallerFilter.first_non_rspec_line end def name From c2481d4d587bd1567b7027e65467f75ee45d0ab9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Nov 2013 09:42:25 -0800 Subject: [PATCH 0355/1179] Document implementation classes as private. --- lib/rspec/mocks/matchers/have_received.rb | 1 + lib/rspec/mocks/matchers/receive.rb | 2 ++ lib/rspec/mocks/matchers/receive_message_chain.rb | 2 +- lib/rspec/mocks/matchers/receive_messages.rb | 1 + lib/rspec/mocks/message_expectation.rb | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 068ee2c9f..2cd810023 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -1,6 +1,7 @@ module RSpec module Mocks module Matchers + # @private class HaveReceived COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice) ARGS_CONSTRAINTS = %w(with) diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 787ef6c25..52e29345c 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -1,6 +1,7 @@ module RSpec module Mocks module Matchers + # @private class Receive def initialize(message, block) @message = message @@ -101,6 +102,7 @@ def move_block_to_last_customization(block) end end + # @private class ExpectationCustomization attr_accessor :block diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index bb54725f5..a8be3fa2d 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module Matchers - #@api private + # @private class ReceiveMessageChain def initialize(chain, &block) @chain = chain diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index 8fad472cf..67795730f 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -1,6 +1,7 @@ module RSpec module Mocks module Matchers + # @private class ReceiveMessages def initialize(message_return_value_hash) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 17e699b4e..498097155 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -34,6 +34,7 @@ def verify_messages_received end end + # @private class MessageExpectation # @private attr_accessor :error_generator, :implementation From ff17d16f16e8c07bce2fe8b9a3e57d77a7654605 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Nov 2013 09:46:26 -0800 Subject: [PATCH 0356/1179] Add changelog for #477. --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index d3583428a..4e93bca8c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,10 @@ Bug Fixes: * Fix verified null object doubles so that they do not wrongly report that they respond to anything. They only respond to methods available on the interface they verify against. (Myron Marston) +* Fix deprecation warning for use of old `:should` syntax w/o explicit + config so that it no longer is silenced by an extension gem such + as rspec-rails when it calls `config.add_stub_and_should_receive_to`. + (Sam Phippen) Enhancements: From 2ef3ee8d7b9b7c599688237a0916c117ed02042e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Nov 2013 09:47:48 -0800 Subject: [PATCH 0357/1179] Add changelog entry. --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4e93bca8c..f95b6554c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -26,6 +26,9 @@ Bug Fixes: config so that it no longer is silenced by an extension gem such as rspec-rails when it calls `config.add_stub_and_should_receive_to`. (Sam Phippen) +* Fix `expect` syntax so that it does not wrongly emit a "You're + overriding a previous implementation for this stub" warning when + you are not actually doing that. (Myron Marston) Enhancements: From 9a8ea8616de80f38f1a5cd1866141ae0f6f468cc Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 29 Nov 2013 12:16:06 +1100 Subject: [PATCH 0358/1179] switch deprecation/warning helpers to support --- spec/spec_helper.rb | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 49c88e189..3c31b85ee 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -33,18 +33,6 @@ def verify_all end end -module DeprecationHelpers - def expect_deprecation_with_call_site(file, line) - expect(RSpec.configuration.reporter).to receive(:deprecation) do |options| - expect(options[:call_site]).to include([file, line].join(':')) - end - end - - def expect_warning_with_call_site(file, line) - expect(Kernel).to receive(:warn).with(/Called from #{file}:#{line}/) - end -end - module VerificationHelpers def prevents(&block) expect(&block).to \ @@ -52,6 +40,8 @@ def prevents(&block) end end +require 'rspec/support/spec' + RSpec.configure do |config| config.mock_with :rspec config.color_enabled = true @@ -81,7 +71,6 @@ def prevents(&block) end config.include VerifyAndResetHelpers - config.include DeprecationHelpers config.include VerificationHelpers end From 5240413f6d0ab7fb636096bfa29ae0be690c1693 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 29 Nov 2013 13:20:43 +1100 Subject: [PATCH 0359/1179] supress legitimate warnings --- spec/rspec/mocks/double_spec.rb | 4 +++- spec/rspec/mocks/instance_method_stasher_spec.rb | 4 ++-- spec/rspec/mocks/serialization_spec.rb | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 98643db25..46e97e404 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -285,7 +285,9 @@ def @double.method_with_default_argument(arg={}); end if RUBY_VERSION.to_f > 1.8 expect(@double.as_null_object.to_a).to eq nil else - expect(@double.as_null_object.to_a).to eq [@double] + with_isolated_stderr do + expect(@double.as_null_object.to_a).to eq [@double] + end end end diff --git a/spec/rspec/mocks/instance_method_stasher_spec.rb b/spec/rspec/mocks/instance_method_stasher_spec.rb index 8972f31d3..1df9e5d79 100644 --- a/spec/rspec/mocks/instance_method_stasher_spec.rb +++ b/spec/rspec/mocks/instance_method_stasher_spec.rb @@ -24,7 +24,7 @@ def obj.hello; :hello_defined_on_singleton_class; end; stashed_method = stasher_for(obj, :hello) stashed_method.stash - def obj.hello; :overridden_hello; end + with_isolated_stderr { def obj.hello; :overridden_hello; end } expect(obj.hello).to eql :overridden_hello stashed_method.restore @@ -39,7 +39,7 @@ def obj.hello; :hello_defined_on_singleton_class; end; stashed_method = stasher_for(obj, :hello) stashed_method.stash - def obj.hello; :overridden_hello; end + with_isolated_stderr { def obj.hello; :overridden_hello; end } stashed_method.restore expect(obj.send(:hello)).to eql :hello_defined_on_singleton_class end diff --git a/spec/rspec/mocks/serialization_spec.rb b/spec/rspec/mocks/serialization_spec.rb index e8b637a04..a99fdf2da 100644 --- a/spec/rspec/mocks/serialization_spec.rb +++ b/spec/rspec/mocks/serialization_spec.rb @@ -61,6 +61,7 @@ def set_stub if compiled_with_psych context 'using Syck as the YAML engine' do before(:each) { ::YAML::ENGINE.yamler = 'syck' } + around(:each) { |example| with_isolated_stderr(&example) } it_behaves_like 'normal YAML serialization' end From 1baaff3e6e54cdca18f47814fa0189150d36f242 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 29 Nov 2013 15:28:11 +1100 Subject: [PATCH 0360/1179] these sometimes emit warnings about to_a but seem harmless --- spec/rspec/mocks/double_spec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 46e97e404..409604cfe 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -270,11 +270,15 @@ def @double.method_with_default_argument(arg={}); end end it "is wrappable in an array" do - expect( Array(@double) ).to eq([@double]) + with_isolated_stderr do + expect( Array(@double) ).to eq([@double]) + end end it "is wrappable in an array when a null object" do - expect( Array(@double.as_null_object) ).to eq [@double] + with_isolated_stderr do + expect( Array(@double.as_null_object) ).to eq [@double] + end end it "responds to to_ary as a null object" do From 3af87b854ccb18bdf92e3f6728cfaeef44d41f13 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 29 Nov 2013 15:29:02 +1100 Subject: [PATCH 0361/1179] squash syntaxes in a 1.8.7 friendly way --- lib/rspec/mocks/configuration.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 8a4d6901e..8e0878c90 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -39,14 +39,15 @@ def add_stub_and_should_receive_to(*modules) end end - def syntax=(values) - if Array(values).include?(:expect) + def syntax=(*values) + syntaxes = values.flatten + if syntaxes.include?(:expect) Syntax.enable_expect else Syntax.disable_expect end - if Array(values).include?(:should) + if syntaxes.include?(:should) Syntax.enable_should else Syntax.disable_should From b3a256abb16a124dac52488f1389aecaebb6aaac Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 29 Nov 2013 20:50:17 -0800 Subject: [PATCH 0362/1179] Use rspec-support's simplecov setup logic. --- .cane | 4 ---- Gemfile | 5 +---- script/test_all | 5 ----- spec/spec_helper.rb | 15 ++++++--------- 4 files changed, 7 insertions(+), 22 deletions(-) delete mode 100644 .cane diff --git a/.cane b/.cane deleted file mode 100644 index 8b82c68e2..000000000 --- a/.cane +++ /dev/null @@ -1,4 +0,0 @@ ---no-doc ---no-style ---no-abc ---gte coverage/.last_run.json,98 diff --git a/Gemfile b/Gemfile index 11e559532..5a42c1e10 100644 --- a/Gemfile +++ b/Gemfile @@ -18,10 +18,7 @@ group :documentation do gem 'github-markup', '0.7.2' end -if RUBY_VERSION >= '1.9.3' - gem 'simplecov' - gem 'cane' -end +gem 'simplecov', '~> 0.8' platforms :jruby do gem "jruby-openssl" diff --git a/script/test_all b/script/test_all index 5408d5bb8..30c35d14a 100755 --- a/script/test_all +++ b/script/test_all @@ -21,11 +21,6 @@ echo "Running all..." bin/rspec spec --format progress --profile -if ruby -e 'exit RUBY_VERSION >= "1.9.3" && RUBY_ENGINE == "ruby"' -then - bin/cane # Uses options from .cane -fi - echo echo "--------------------------------------------------------------------" echo diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3c31b85ee..be0c61486 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,18 +1,15 @@ +require 'rspec/support/spec' + +RSpec::Support::Spec.setup_simplecov do + minimum_coverage 97 +end + require 'yaml' begin require 'psych' rescue LoadError end -begin - require 'simplecov' - - SimpleCov.start do - add_filter "bundle" - end -rescue LoadError -end unless ENV['NO_COVERAGE'] || RUBY_VERSION < '1.9.3' - RSpec::Matchers.define :include_method do |expected| match do |actual| actual.map { |m| m.to_s }.include?(expected.to_s) From 184ab52b15849ae0d1801e8db5561b8a21f36687 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 29 Nov 2013 20:50:27 -0800 Subject: [PATCH 0363/1179] Remove old rcov tasks. --- Rakefile | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/Rakefile b/Rakefile index a3c88f868..6f301c88c 100644 --- a/Rakefile +++ b/Rakefile @@ -15,33 +15,6 @@ end Cucumber::Rake::Task.new(:cucumber) -if RUBY_VERSION.to_f == 1.8 - namespace :rcov do - desc "Run all examples using rcov" - RSpec::Core::RakeTask.new(:spec) do |t| - t.rcov = true - t.rcov_opts = %[-Ilib -Ispec --exclude "gems/*,features"] - t.rcov_opts << %[--text-report --sort coverage --no-html --aggregate coverage.data] - end - - desc "Run cucumber features using rcov" - Cucumber::Rake::Task.new(:cucumber) do |t| - t.cucumber_opts = %w{--format progress} - t.rcov = true - t.rcov_opts = %[-Ilib -Ispec --exclude "gems/*,features"] - t.rcov_opts << %[--text-report --sort coverage --aggregate coverage.data] - end - - task :cleanup do - rm_rf 'coverage.data' - end - end -end - - -desc "run specs and cukes with rcov" -task :rcov => ["rcov:cleanup", "rcov:spec", "rcov:cucumber"] - task :clobber do rm_rf 'pkg' rm_rf 'tmp' From ca4e3d3638987883f4809f0df0d19b57f881920c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 29 Nov 2013 22:51:55 -0800 Subject: [PATCH 0364/1179] Update changelog. [ci skip] --- Changelog.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index f95b6554c..d73034898 100644 --- a/Changelog.md +++ b/Changelog.md @@ -32,9 +32,9 @@ Bug Fixes: Enhancements: -* Add receive_message_chain which provides the functionality of the old - stub_chain for the new allow/expect syntax. Use it like so: allow(...).to - receive_message_chain(:foo, :bar, :bazz). (Sam Phippen). +* Add `receive_message_chain` which provides the functionality of the old + `stub_chain` for the new allow/expect syntax. Use it like so: `allow(...).to + receive_message_chain(:foo, :bar, :bazz)`. (Sam Phippen). * Change argument matchers to use `===` as their primary matching protocol, as their semantics mirror that of a case or rescue statement (which uses `===` for matching). (Myron Marston) @@ -73,7 +73,7 @@ Enhancements: * Provide `object_double` to create verified doubles of specific object instances. (Xavier Shay) * Provide `verify_partial_doubles` configuration that provides `object_double` - like verification behaviour on partial mocks. (Xavier Shay) + like verification behaviour on partial doubles. (Xavier Shay) * Improved performance of double creation, particularly those with many attributes. (Xavier Shay) * Default value of `transfer_nested_constants` option for constant stubbing can From 1ec31b581035c83876972832a5b51a6c5d4f8b9d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 30 Nov 2013 20:49:45 -0800 Subject: [PATCH 0365/1179] Update `have_received` matcher to latest protocol. --- lib/rspec/mocks/matchers/have_received.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 2cd810023..c1743a322 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -36,7 +36,7 @@ def failure_message generate_failure_message end - def negative_failure_message + def failure_message_when_negated generate_failure_message end From f67a4ca0600a9ba0559a8bbdc6693d0abf1fa712 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 4 Dec 2013 15:46:53 +1100 Subject: [PATCH 0366/1179] change unstub warning to remove mention of expect --- lib/rspec/mocks/syntax.rb | 11 ++++++++--- spec/rspec/mocks/configuration_spec.rb | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 3cd1168c4..469b5d2e6 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -10,11 +10,16 @@ def self.warn_about_should! end # @api private - def self.warn_unless_should_configured(method_name) + def self.warn_unless_should_configured(method_name, expect_replacement = true) if @warn_about_should + if expect_replacement + replacement = "the new `:expect` syntax or explicitly enable `:should`" + else + replacement = "the `:should` syntax by explicitly enabling it" + end RSpec.deprecate( "Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax", - :replacement => "the new `:expect` syntax or explicitly enable `:should`" + :replacement => replacement ) @warn_about_should = false @@ -51,7 +56,7 @@ def stub(message_or_hash, opts={}, &block) end def unstub(message) - ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, false) ::RSpec::Mocks.space.proxy_for(self).remove_stub(message) end diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index cf6177963..15b0b0a07 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -162,6 +162,20 @@ def sandboxed o2.stub(:faces) end + it "warns about unstubbing once, regardless of how many times it is called" do + expect(RSpec).to receive(:deprecate).with(/Using.*without explicitly enabling/, + {:replacement => "the `:should` syntax by explicitly enabling it"}) + o = Object.new + o2 = Object.new + + allow(o).to receive(:faces) + allow(o2).to receive(:faces) + + o.unstub(:faces) + o2.unstub(:faces) + end + + it "doesn't warn about stubbing after a reset and setting should" do expect(RSpec).not_to receive(:deprecate) RSpec::Mocks.configuration.reset_syntaxes_to_default From 0ed4de10a4769393233d665fe05be01e7460bd7a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 4 Dec 2013 16:19:12 +1100 Subject: [PATCH 0367/1179] Update Rubinius on Travis Travis actually dropped (due to rvm dropping) support for rbx-1.8 and rbx-1.9. There is now the option to have 2.2.1 and head. We should do something about this. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8a2931a5d..590e722f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,12 @@ rvm: - ree - jruby-18mode - jruby-19mode - - rbx-18mode - - rbx-19mode + - rbx-2.2.1 + - rbx - 2.0.0 - 2.1.0-preview2 matrix: allow_failures: - - rvm: rbx-18mode - - rvm: rbx-19mode + - rvm: rbx-2.2.1 + - rvm: rbx From f628d11b8c0ef452fb2859e9264648159631d26b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 5 Dec 2013 21:52:28 -0800 Subject: [PATCH 0368/1179] Cleanup matcher support. Now that rspec-expectations matchers implement #===, there's no need to treat them special here. --- lib/rspec/mocks/argument_list_matcher.rb | 15 +-------- lib/rspec/mocks/argument_matchers.rb | 11 ------- spec/rspec/mocks/argument_expectation_spec.rb | 32 ------------------- 3 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 spec/rspec/mocks/argument_expectation_spec.rb diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index d8069a13c..edd8b1805 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -51,7 +51,7 @@ def initialize(*expected_args) when ArgumentMatchers::NoArgsMatcher @matchers = [] else - @matchers = expected_args.collect {|arg| matcher_for(arg)} + @matchers = expected_args end end @@ -68,19 +68,6 @@ def args_match?(*args) private - def matcher_for(arg) - return ArgumentMatchers::MatcherMatcher.new(arg) if is_matcher?(arg) - arg - end - - def is_matcher?(object) - return false if object.respond_to?(:i_respond_to_everything_so_im_not_really_a_matcher) - - [:failure_message_for_should, :failure_message].any? do |msg| - object.respond_to?(msg) - end && object.respond_to?(:matches?) - end - def matchers_match?(*args) return false unless @matchers.count == args.count diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index daca3251e..098291750 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -230,17 +230,6 @@ def ===(value) end end - # @api private - class MatcherMatcher - def initialize(matcher) - @matcher = matcher - end - - def ===(value) - @matcher.matches?(value) - end - end - # @api private class InstanceOf def initialize(klass) diff --git a/spec/rspec/mocks/argument_expectation_spec.rb b/spec/rspec/mocks/argument_expectation_spec.rb deleted file mode 100644 index e0a9a92ad..000000000 --- a/spec/rspec/mocks/argument_expectation_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'spec_helper' - -module RSpec - module Mocks - describe ArgumentListMatcher do - let(:argument_expectation) { RSpec::Mocks::ArgumentListMatcher.new } - let(:obj) { double("matcher") } - - it "considers an object that responds to #matches? and #failure_message_for_should to be a matcher" do - obj.stub(:matches?) - obj.stub(:failure_message_for_should) - expect(argument_expectation.send(:is_matcher?, obj)).to be_truthy - end - - it "considers an object that responds to #matches? and #failure_message to be a matcher for backward compatibility" do - obj.stub(:matches?) - obj.stub(:failure_message) - expect(argument_expectation.send(:is_matcher?, obj)).to be_truthy - end - - it "does NOT consider an object that only responds to #matches? to be a matcher" do - obj.stub(:matches?) - expect(argument_expectation.send(:is_matcher?, obj)).to be_falsey - end - - it "does not consider a null object to be a matcher" do - obj.as_null_object - expect(argument_expectation.send(:is_matcher?, obj)).to be_falsey - end - end - end -end From d81cfc07d88313a0ebf0ce3d9b01bd39f723b1f3 Mon Sep 17 00:00:00 2001 From: Vinicius Horewicz Date: Fri, 6 Dec 2013 15:14:30 +0100 Subject: [PATCH 0369/1179] Fix Markdown link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6b343ba9..c09a4620e 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ book = instance_double("Book", :pages => 250) Verifying doubles have some clever tricks to enable you to both test in isolation without your dependencies loaded while still being able to validate -them against real objects. More detail is available in (their -documentation)[https://github.com/rspec/rspec-mocks/blob/master/features/verifying_doubles]. +them against real objects. More detail is available in [their +documentation](https://github.com/rspec/rspec-mocks/blob/master/features/verifying_doubles). ## Method Stubs From 2e0ab0da172723bee4d3de05089dccfbc7ea55ee Mon Sep 17 00:00:00 2001 From: Vinicius Horewicz Date: Fri, 6 Dec 2013 15:24:21 +0100 Subject: [PATCH 0370/1179] Fix feature to load dependencies as expected The spec wasn't loading the dependencies as it should. Even with `ConsoleNotifier` defined, the spec was running in isolation. --- features/verifying_doubles/instance_doubles.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifying_doubles/instance_doubles.feature b/features/verifying_doubles/instance_doubles.feature index 167b0690d..adc12ade4 100644 --- a/features/verifying_doubles/instance_doubles.feature +++ b/features/verifying_doubles/instance_doubles.feature @@ -76,7 +76,7 @@ Feature: Using an instance double end """ - When I run `rspec spec/unit/user_spec.rb` + When I run `rspec -r./spec/spec_helper spec/unit/user_spec.rb` Then the examples should all pass Scenario: spec fails with dependencies loaded and method unimplemented From 30fb2f18fd4001a74c06986935f74595ee10adff Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 7 Dec 2013 07:36:43 +1100 Subject: [PATCH 0371/1179] change unstub message to include replacemnt --- lib/rspec/mocks/syntax.rb | 9 ++------- spec/rspec/mocks/configuration_spec.rb | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 469b5d2e6..28438efce 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -10,13 +10,8 @@ def self.warn_about_should! end # @api private - def self.warn_unless_should_configured(method_name, expect_replacement = true) + def self.warn_unless_should_configured(method_name ,replacement = "the new `:expect` syntax or explicitly enable `:should`") if @warn_about_should - if expect_replacement - replacement = "the new `:expect` syntax or explicitly enable `:should`" - else - replacement = "the `:should` syntax by explicitly enabling it" - end RSpec.deprecate( "Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax", :replacement => replacement @@ -56,7 +51,7 @@ def stub(message_or_hash, opts={}, &block) end def unstub(message) - ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, false) + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to_receive(:#{message}).and_call_original` or explicitly enable `:should`") ::RSpec::Mocks.space.proxy_for(self).remove_stub(message) end diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 15b0b0a07..2b62b745d 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -164,7 +164,7 @@ def sandboxed it "warns about unstubbing once, regardless of how many times it is called" do expect(RSpec).to receive(:deprecate).with(/Using.*without explicitly enabling/, - {:replacement => "the `:should` syntax by explicitly enabling it"}) + {:replacement => "`allow(...).to_receive(:faces).and_call_original` or explicitly enable `:should`"}) o = Object.new o2 = Object.new From c5b008093e91195d39fcf98450d4d7cdafd8a01e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 7 Dec 2013 08:50:19 -0800 Subject: [PATCH 0372/1179] Add specs showing how `and_call_original` can be used like `unstub`. --- spec/rspec/mocks/and_call_original_spec.rb | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 7279f61af..1a9573d32 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -21,6 +21,29 @@ def self.new_instance let(:instance) { klass.new } + context "when a method that exists has been stubbed previously" do + before { allow(instance).to receive(:meth_1).and_return(:override) } + + it 'restores the original behavior' do + expect { + allow(instance).to receive(:meth_1).and_call_original + }.to change(instance, :meth_1).from(:override).to(:original) + end + end + + context "when a non-existant method has been stubbed previously" do + it 'restores the original NameError behavior' do + message = nil + expect { instance.abcd }.to raise_error(NameError) { |msg| message = msg.message } + + allow(instance).to receive(:abcd).and_return(:override) + expect(instance.abcd).to eq(:override) + + allow(instance).to receive(:abcd).and_call_original + expect { instance.abcd }.to raise_error(NameError).with_message(message) + end + end + it 'passes the received message through to the original method' do expect(instance).to receive(:meth_1).and_call_original expect(instance.meth_1).to eq(:original) From b4cc83adebd16208db75579a6a278bcd476f13e1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 7 Dec 2013 08:53:38 -0800 Subject: [PATCH 0373/1179] Tweak deprecation message a bit. The deprecation is worded in general terms so it seems odd for the replacement to be specific about which message (particularly since it's not specific about which object is passed to `allow`). --- lib/rspec/mocks/syntax.rb | 2 +- spec/rspec/mocks/configuration_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 28438efce..5f389de15 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -51,7 +51,7 @@ def stub(message_or_hash, opts={}, &block) end def unstub(message) - ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to_receive(:#{message}).and_call_original` or explicitly enable `:should`") + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`") ::RSpec::Mocks.space.proxy_for(self).remove_stub(message) end diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 2b62b745d..dec4a07ef 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -164,7 +164,7 @@ def sandboxed it "warns about unstubbing once, regardless of how many times it is called" do expect(RSpec).to receive(:deprecate).with(/Using.*without explicitly enabling/, - {:replacement => "`allow(...).to_receive(:faces).and_call_original` or explicitly enable `:should`"}) + {:replacement => "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`"}) o = Object.new o2 = Object.new From 952d954dae9fb662c5759112f27bc1a2bbe9d5d3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 6 Dec 2013 14:19:56 +1100 Subject: [PATCH 0374/1179] fixup unstubing of sub classes when super classes are stubbed, fixes #474 --- Changelog.md | 2 + lib/rspec/mocks/any_instance/recorder.rb | 47 ++++++++++++++++++------ spec/rspec/mocks/any_instance_spec.rb | 16 ++++++++ 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/Changelog.md b/Changelog.md index d73034898..70da1c27b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -29,6 +29,8 @@ Bug Fixes: * Fix `expect` syntax so that it does not wrongly emit a "You're overriding a previous implementation for this stub" warning when you are not actually doing that. (Myron Marston) +* Fix `any_instance.unstub` when used on sub classes for whom the super + class has had `any_instance.stub` invoked on. (Jon Rowe) Enhancements: diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index cd188d9b8..140ec18b3 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -125,10 +125,36 @@ def build_alias_method_name(method_name) end def already_observing?(method_name) - @observed_methods.include?(method_name) + @observed_methods.include?(method_name) || super_class_observing?(method_name) end - private + protected + + def stop_observing!(method_name) + restore_method!(method_name) + @observed_methods.delete(method_name) + super_class_observers_for(method_name).each do |ancestor| + ::RSpec::Mocks.any_instance_recorder_for(ancestor).stop_observing!(method_name) + end + end + + private + + def ancestor_is_an_observer?(method_name) + lambda do |ancestor| + unless ancestor == @klass + ::RSpec::Mocks.any_instance_recorder_for(ancestor).already_observing?(method_name) + end + end + end + + def super_class_observers_for(method_name) + @klass.ancestors.select(&ancestor_is_an_observer?(method_name)) + end + + def super_class_observing?(method_name) + @klass.ancestors.any?(&ancestor_is_an_observer?(method_name)) + end def normalize_chain(*args) args.shift.to_s.split('.').map {|s| s.to_sym}.reverse.each {|a| args.unshift a} @@ -150,11 +176,13 @@ def restore_method!(method_name) end def restore_original_method!(method_name) - alias_method_name = build_alias_method_name(method_name) - @klass.class_exec do - remove_method method_name - alias_method method_name, alias_method_name - remove_method alias_method_name + if @klass.instance_method(method_name).owner == @klass + alias_method_name = build_alias_method_name(method_name) + @klass.class_exec do + remove_method method_name + alias_method method_name, alias_method_name + remove_method alias_method_name + end end end @@ -175,11 +203,6 @@ def public_protected_or_private_method_defined?(method_name) MethodReference.method_defined_at_any_visibility?(@klass, method_name) end - def stop_observing!(method_name) - restore_method!(method_name) - @observed_methods.delete(method_name) - end - def observe!(method_name) if RSpec::Mocks.configuration.verify_partial_doubles? raise MockExpectationError unless @klass.method_defined?(method_name) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 4f948959d..1e464afc7 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -97,6 +97,9 @@ def private_method; :private_method_return_value; end end context "behaves as 'every instance'" do + let(:super_class) { Class.new { def foo; 'bar'; end } } + let(:sub_class) { Class.new(super_class) } + it "stubs every instance in the spec" do klass.any_instance.stub(:foo).and_return(result = Object.new) expect(klass.new.foo).to eq(result) @@ -115,6 +118,19 @@ def private_method; :private_method_return_value; end foo = 'foo'.freeze expect(foo.dup.concat 'bar').to eq 'foobar' end + + it 'handles stubbing on super and subclasses' do + super_class.any_instance.stub(:foo) + sub_class.any_instance.stub(:foo).and_return('baz') + expect(sub_class.new.foo).to eq('baz') + end + + it 'handles method restoration on subclasses' do + super_class.any_instance.stub(:foo) + sub_class.any_instance.stub(:foo) + sub_class.any_instance.unstub(:foo) + expect(sub_class.new.foo).to eq("bar") + end end context "with argument matching" do From 9124d8c25f8468140828f6c72b95795d9ba593dd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 14 Dec 2013 21:34:29 -0800 Subject: [PATCH 0375/1179] Add rubysl for rbx. --- .travis.yml | 8 ++------ Gemfile | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 590e722f0..7bf36ef1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,15 +4,11 @@ rvm: - 1.8.7 - 1.9.2 - 1.9.3 + - 2.0.0 + - 2.1.0-preview2 - ree - jruby-18mode - jruby-19mode - rbx-2.2.1 - rbx - - 2.0.0 - - 2.1.0-preview2 -matrix: - allow_failures: - - rvm: rbx-2.2.1 - - rvm: rbx diff --git a/Gemfile b/Gemfile index 5a42c1e10..13e8465b1 100644 --- a/Gemfile +++ b/Gemfile @@ -24,4 +24,8 @@ platforms :jruby do gem "jruby-openssl" end +platforms :rbx do + gem 'rubysl' +end + eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') From 92b9f9e46b26d47c69294835b780de2bd4c0f89a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 15 Dec 2013 18:59:04 +1100 Subject: [PATCH 0376/1179] add rubysl-test-unit for travis --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 13e8465b1..ad25bcc1e 100644 --- a/Gemfile +++ b/Gemfile @@ -26,6 +26,7 @@ end platforms :rbx do gem 'rubysl' + gem 'rubysl-test-unit' end eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') From 952c0e8c9dc61bb58d51b9b87fbc1efb29eb7791 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 19 Dec 2013 21:33:07 -0800 Subject: [PATCH 0377/1179] `red carpet` won't install on jruby. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index ad25bcc1e..bf1f9c41c 100644 --- a/Gemfile +++ b/Gemfile @@ -14,7 +14,7 @@ end ### deps for rdoc.info group :documentation do gem 'yard', '0.8.0', :require => false - gem 'redcarpet', '2.1.1' + gem 'redcarpet', '2.1.1' unless RUBY_PLATFORM == 'java' gem 'github-markup', '0.7.2' end From 3bb9afce84ce700410e195f348b3e0efa0d86eb3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 19 Dec 2013 21:35:46 -0800 Subject: [PATCH 0378/1179] Fix spec that was failing on jruby. On jruby, the error for calling an undefined method is a bit inconsistent: The first time: "undefined method `abcd' for #<#:0x70019a64>" The second time: "undefined local variable or method `abcd' for #<#:0x70019a64>" --- spec/rspec/mocks/and_call_original_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 1a9573d32..d7aa3a085 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -33,14 +33,13 @@ def self.new_instance context "when a non-existant method has been stubbed previously" do it 'restores the original NameError behavior' do - message = nil - expect { instance.abcd }.to raise_error(NameError) { |msg| message = msg.message } + expect { instance.abcd }.to raise_error(NameError).with_message(/abcd/) allow(instance).to receive(:abcd).and_return(:override) expect(instance.abcd).to eq(:override) allow(instance).to receive(:abcd).and_call_original - expect { instance.abcd }.to raise_error(NameError).with_message(message) + expect { instance.abcd }.to raise_error(NameError).with_message(/abcd/) end end From 087ea72d42c694c708aaa5bb0e26b98cc2894745 Mon Sep 17 00:00:00 2001 From: Ashish Dixit Date: Thu, 19 Dec 2013 13:47:22 -0600 Subject: [PATCH 0379/1179] Update the documentation to use the new expectation syntax. * Update cukes to use the new expectation syntax where possible. * Update RDoc documentation to use the `expect` syntax where applicable. * Add RDoc documentation for `syntax=` & `syntax` methods. * Rename cukes to reflect new syntax. --- features/README.md | 4 +- features/argument_matchers/README.md | 10 ++-- features/argument_matchers/explicit.feature | 12 ++-- .../general_matchers.feature | 8 +-- .../argument_matchers/type_matchers.feature | 4 +- features/message_expectations/README.md | 56 ++++++++++--------- features/method_stubs/README.md | 3 +- ....feature => allow_any_instance_of.feature} | 24 ++++---- ....feature => receive_message_chain.feature} | 6 +- features/method_stubs/to_ary.feature | 2 +- lib/rspec/mocks/argument_list_matcher.rb | 4 +- lib/rspec/mocks/argument_matchers.rb | 34 +++++------ lib/rspec/mocks/configuration.rb | 23 ++++++++ lib/rspec/mocks/message_expectation.rb | 52 ++++++++--------- 14 files changed, 134 insertions(+), 108 deletions(-) rename features/method_stubs/{any_instance.feature => allow_any_instance_of.feature} (78%) rename features/method_stubs/{stub_chain.feature => receive_message_chain.feature} (86%) diff --git a/features/README.md b/features/README.md index b70a58552..74fe2a955 100644 --- a/features/README.md +++ b/features/README.md @@ -36,7 +36,7 @@ them against real objects. A method stub is an instruction to an object (real or test double) to return a known value in response to a message: - die.stub(:roll) { 3 } + allow(die).to receive(:roll) { 3 } This tells the `die` object to return the value `3` when it receives the `roll` message. @@ -53,7 +53,7 @@ specific message during the course of a code example: account = Account.new account.logger = logger - logger.should_receive(:account_closed).with(account) + expect(logger).to receive(:account_closed).with(account) account.close end diff --git a/features/argument_matchers/README.md b/features/argument_matchers/README.md index 1d1c9e949..423ba997f 100644 --- a/features/argument_matchers/README.md +++ b/features/argument_matchers/README.md @@ -4,10 +4,10 @@ Argument matchers can be used: * In stubs to constrain the scope of the stubbed method - obj.stub(:foo).with(:bar) do |arg| + allow(obj).to receive(:foo).with(:bar) do |arg| #do something for :bar end - obj.stub(:foo).with(:baz) do |arg| + allow(obj).to receive(:foo).with(:baz) do |arg| #do something for :baz end @@ -17,11 +17,11 @@ Argument matchers can be used: obj = double() #expect a message with given args - obj.should_receive(:message).with('an argument') + expect(obj).to receive(:message).with('an argument') If more control is needed, one can use a block - obj.should_receive(:message) do |arg1, arg2| + expect(obj).to receive(:message) do |arg1, arg2| # set expectations about the args in this block # and optionally set a return value - end \ No newline at end of file + end diff --git a/features/argument_matchers/explicit.feature b/features/argument_matchers/explicit.feature index 2c6d195d1..2f8ccbc08 100644 --- a/features/argument_matchers/explicit.feature +++ b/features/argument_matchers/explicit.feature @@ -8,15 +8,15 @@ Feature: explicit arguments describe "stubbed explicit arguments" do it "works on stubs" do object = Object.new - object.stub(:foo).with(:this) do |arg| + allow(object).to receive(:foo).with(:this) do |arg| "got this" end - object.stub(:foo).with(:that) do |arg| + allow(object).to receive(:foo).with(:that) do |arg| "got that" end - object.foo(:this).should eq("got this") - object.foo(:that).should eq("got that") + expect(object.foo(:this)).to eq("got this") + expect(object.foo(:that)).to eq("got that") end it "works on doubles and expectations" do @@ -36,10 +36,10 @@ Feature: explicit arguments describe "stubbed multiple explicit arguments" do it "works on stubs" do object = Object.new - object.stub(:foo).with(:this) do |arg| + allow(object).to receive(:foo).with(:this) do |arg| "got this" end - object.stub(:foo).with(:this, :that) do |arg1, arg2| + allow(object).to receive(:foo).with(:this, :that) do |arg1, arg2| "got this and that" end diff --git a/features/argument_matchers/general_matchers.feature b/features/argument_matchers/general_matchers.feature index 0522d0397..117f7fa5b 100644 --- a/features/argument_matchers/general_matchers.feature +++ b/features/argument_matchers/general_matchers.feature @@ -11,7 +11,7 @@ Feature: General matchers describe "stubbed anything() args spec" do it "works" do object = Object.new - object.stub(:foo).with(anything) do + allow(object).to receive(:foo).with(anything) do "anything" end @@ -29,7 +29,7 @@ Feature: General matchers describe "stubbed any_args() args spec" do it "works" do object = Object.new - object.stub(:foo).with(any_args) do + allow(object).to receive(:foo).with(any_args) do "anything" end @@ -48,10 +48,10 @@ Feature: General matchers describe "stubbed no_args() args spec" do it "works for no args" do object = Object.new - object.stub(:foo).with(no_args) do + allow(object).to receive(:foo).with(no_args) do "nothing" end - object.stub(:foo).with(anything) do + allow(object).to receive(:foo).with(anything) do "something" end diff --git a/features/argument_matchers/type_matchers.feature b/features/argument_matchers/type_matchers.feature index 46d3ab442..7b8183a93 100644 --- a/features/argument_matchers/type_matchers.feature +++ b/features/argument_matchers/type_matchers.feature @@ -10,10 +10,10 @@ Feature: stub with argument constraints describe "stubbed an_instance_of() args spec" do it "works" do object = Object.new - object.stub(:foo).with(an_instance_of(Symbol)) do + allow(object).to receive(:foo).with(an_instance_of(Symbol)) do "symbol" end - object.stub(:foo).with(an_instance_of(String)) do + allow(object).to receive(:foo).with(an_instance_of(String)) do "string" end diff --git a/features/message_expectations/README.md b/features/message_expectations/README.md index 35f39ed05..b0015c41e 100644 --- a/features/message_expectations/README.md +++ b/features/message_expectations/README.md @@ -4,12 +4,14 @@ obj = double() # expect a message - obj.should_receive(:message) + expect(obj).to receive(:message) # specify a return value - obj.should_receive(:message) { :value } - obj.should_receive(:message => :value) - obj.should_receive(:message).and_return(:value) + expect(obj).to receive(:message) { :value } + expect(obj).to receive(:message).and_return(:value) + + # specify multiple message/return value pairs + expect(obj).to receive_messages(:message => :value, :another_message => :another_value) These forms are somewhat interchangeable. The difference is that the block contents are evaluated lazily when the `obj` receives the @@ -17,57 +19,57 @@ block contents are evaluated lazily when the `obj` receives the ### Fake implementation - obj.should_receive(:message) do |arg1, arg2| + expect(obj).to receive(:message) do |arg1, arg2| # set expectations about the args in this block # and set a return value end ### Using the original implementation - obj.should_receive(:message).and_call_original + expect(obj).to receive(:message).and_call_original ### Raising/Throwing - obj.should_receive(:message).and_raise("this error") - obj.should_receive(:message).and_throw(:this_symbol) + expect(obj).to receive(:message).and_raise("this error") + expect(obj).to receive(:message).and_throw(:this_symbol) You can also use the block format: - obj.should_receive(:message) { raise "this error" } - obj.should_receive(:message) { throw :this_symbol } + expect(obj).to receive(:message) { raise "this error" } + expect(obj).to receive(:message) { throw :this_symbol } ### Argument constraints #### Explicit arguments - obj.should_receive(:message).with('an argument') - obj.should_receive(:message).with('more_than', 'one_argument') + expect(obj).to receive(:message).with('an argument') + expect(obj).to receive(:message).with('more_than', 'one_argument') #### Argument matchers - obj.should_receive(:message).with(anything()) - obj.should_receive(:message).with(an_instance_of(Money)) - obj.should_receive(:message).with(hash_including(:a => 'b')) + expect(obj).to receive(:message).with(anything()) + expect(obj).to receive(:message).with(an_instance_of(Money)) + expect(obj).to receive(:message).with(hash_including(:a => 'b')) #### Regular expressions - obj.should_receive(:message).with(/abc/) + expect(obj).to receive(:message).with(/abc/) ### Counts - obj.should_receive(:message).once - obj.should_receive(:message).twice - obj.should_receive(:message).exactly(3).times + expect(obj).to receive(:message).once + expect(obj).to receive(:message).twice + expect(obj).to receive(:message).exactly(3).times - obj.should_receive(:message).at_least(:once) - obj.should_receive(:message).at_least(:twice) - obj.should_receive(:message).at_least(n).times + expect(obj).to receive(:message).at_least(:once) + expect(obj).to receive(:message).at_least(:twice) + expect(obj).to receive(:message).at_least(n).times - obj.should_receive(:message).at_most(:once) - obj.should_receive(:message).at_most(:twice) - obj.should_receive(:message).at_most(n).times + expect(obj).to receive(:message).at_most(:once) + expect(obj).to receive(:message).at_most(:twice) + expect(obj).to receive(:message).at_most(n).times ### Ordering - obj.should_receive(:one).ordered - obj.should_receive(:two).ordered + expect(obj).to receive(:one).ordered + expect(obj).to receive(:two).ordered diff --git a/features/method_stubs/README.md b/features/method_stubs/README.md index 81e6e883a..383190687 100644 --- a/features/method_stubs/README.md +++ b/features/method_stubs/README.md @@ -20,7 +20,7 @@ block contents are evaluated lazily when the `obj` receives the allow(obj).to receive(:message) do |arg1, arg2| # set expectations about the args in this block - # and/or return value + # and/or return value end obj.stub(:message) do |arg1, arg2| @@ -49,6 +49,7 @@ You can also use the block format: #### Explicit arguments allow(obj).to receive(:message).with('an argument') { ... } + allow(obj).to receive(:message).with('more than', 'an argument') { ... } obj.stub(:message).with('an argument') { ... } obj.stub(:message).with('more_than', 'one_argument') { ... } diff --git a/features/method_stubs/any_instance.feature b/features/method_stubs/allow_any_instance_of.feature similarity index 78% rename from features/method_stubs/any_instance.feature rename to features/method_stubs/allow_any_instance_of.feature index ce3bd9602..684642776 100644 --- a/features/method_stubs/any_instance.feature +++ b/features/method_stubs/allow_any_instance_of.feature @@ -1,6 +1,6 @@ Feature: stub on any instance of a class - Use `any_instance.stub` on a class to tell any instance of that class to + Use `allow_any_instance_of` on a class to tell any instance of that class to return a value (or values) in response to a given message. If no instance receives the message, nothing happens. @@ -10,12 +10,12 @@ Feature: stub on any instance of a class to the object that receives a message in your test. For more information, see the message_expectations/allow_any_instance_of feature. - Scenario: any_instance stub with a single return value + Scenario: Stubbing any instance of an object with a single return value Given a file named "example_spec.rb" with: """ruby - describe "any_instance.stub" do + describe "stubbing any instance" do it "returns the specified value on any instance of the class" do - Object.any_instance.stub(:foo).and_return(:return_value) + allow_any_instance_of(Object).to receive(:foo).and_return(:return_value) o = Object.new expect(o.foo).to eq(:return_value) @@ -31,7 +31,7 @@ Feature: stub on any instance of a class describe "any_instance.stub" do context "with a hash" do it "returns the hash values on any instance of the class" do - Object.any_instance.stub(:foo => 'foo', :bar => 'bar') + allow_any_instance_of(Object).to receive_messages(:foo => 'foo', :bar => 'bar') o = Object.new expect(o.foo).to eq('foo') @@ -43,14 +43,14 @@ Feature: stub on any instance of a class When I run `rspec example_spec.rb` Then the examples should all pass - Scenario: any_instance stub with specific arguments matchers + Scenario: Stubbing any instance of an object with specific arguments matchers Given a file named "example_spec.rb" with: """ruby - describe "any_instance.stub" do + describe "stubbing any instance" do context "with arguments" do it "returns the stubbed value when arguments match" do - Object.any_instance.stub(:foo).with(:param_one, :param_two).and_return(:result_one) - Object.any_instance.stub(:foo).with(:param_three, :param_four).and_return(:result_two) + allow_any_instance_of(Object).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) + allow_any_instance_of(Object).to receive(:foo).with(:param_three, :param_four).and_return(:result_two) o = Object.new expect(o.foo(:param_one, :param_two)).to eq(:result_one) @@ -112,21 +112,21 @@ Feature: stub on any instance of a class describe "stubbing a chain of methods" do context "given symbols representing methods" do it "returns the correct value" do - Object.any_instance.stub_chain(:one, :two, :three).and_return(:four) + allow_any_instance_of(Object).to receive_message_chain(:one, :two, :three).and_return(:four) expect(Object.new.one.two.three).to eq(:four) end end context "given a hash at the end" do it "returns the correct value" do - Object.any_instance.stub_chain(:one, :two, :three => :four) + allow_any_instance_of(Object).to receive_message_chain(:one, :two, :three=> :four) expect(Object.new.one.two.three).to eq(:four) end end context "given a string of methods separated by dots" do it "returns the correct value" do - Object.any_instance.stub_chain("one.two.three").and_return(:four) + allow_any_instance_of(Object).to receive_message_chain("one.two.three").and_return(:four) expect(Object.new.one.two.three).to eq(:four) end end diff --git a/features/method_stubs/stub_chain.feature b/features/method_stubs/receive_message_chain.feature similarity index 86% rename from features/method_stubs/stub_chain.feature rename to features/method_stubs/receive_message_chain.feature index 68b0b7e42..aa298dc21 100644 --- a/features/method_stubs/stub_chain.feature +++ b/features/method_stubs/receive_message_chain.feature @@ -27,21 +27,21 @@ Feature: stub a chain of methods context "given symbols representing methods" do it "returns the correct value" do - subject.stub_chain(:one, :two, :three).and_return(:four) + allow(subject).to receive_message_chain(:one, :two, :three).and_return(:four) expect(subject.one.two.three).to eq(:four) end end context "given a hash at the end" do it "returns the correct value" do - subject.stub_chain(:one, :two, :three => :four) + allow(subject).to receive_message_chain(:one, :two, :three => :four) expect(subject.one.two.three).to eq(:four) end end context "given a string of methods separated by dots" do it "returns the correct value" do - subject.stub_chain("one.two.three").and_return(:four) + allow(subject).to receive_message_chain("one.two.three").and_return(:four) expect(subject.one.two.three).to eq(:four) end end diff --git a/features/method_stubs/to_ary.feature b/features/method_stubs/to_ary.feature index 66f123ccc..6d8327b68 100644 --- a/features/method_stubs/to_ary.feature +++ b/features/method_stubs/to_ary.feature @@ -18,7 +18,7 @@ Feature: double handling to_ary describe "#to_ary" do shared_examples "to_ary" do it "can be overridden with a stub" do - obj.stub(:to_ary) { :non_nil_value } + allow(obj).to receive(:to_ary) { :non_nil_value } expect(obj.to_ary).to be(:non_nil_value) end diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index edd8b1805..e9a018217 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -5,13 +5,13 @@ module Mocks # Wrapper for matching arguments against a list of expected values. Used by # the `with` method on a `MessageExpectation`: # - # object.should_receive(:message).with(:a, 'b', 3) + # expect(object).to receive(:message).with(:a, 'b', 3) # object.message(:a, 'b', 3) # # Values passed to `with` can be literal values or argument matchers that # match against the real objects .e.g. # - # object.should_receive(:message).with(hash_including(:a => 'b')) + # expect(object).to receive(:message).with(hash_including(:a => 'b')) # # Can also be used directly to match the contents of any `Array`. This # enables 3rd party mocking libs to take advantage of rspec's argument diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 098291750..9372f2967 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -11,11 +11,11 @@ module Mocks # @see ArgumentListMatcher module ArgumentMatchers # Matches any args at all. Supports a more explicit variation of - # `object.should_receive(:message)` + # `expect(object).to receive(:message)` # # @example # - # object.should_receive(:message).with(any_args) + # expect(object).to receive(:message).with(any_args) def any_args AnyArgsMatcher.new end @@ -24,7 +24,7 @@ def any_args # # @example # - # object.should_receive(:message).with(anything) + # expect(object).to receive(:message).with(anything) def anything AnyArgMatcher.new end @@ -33,7 +33,7 @@ def anything # # @example # - # object.should_receive(:message).with(no_args) + # expect(object).to receive(:message).with(no_args) def no_args NoArgsMatcher.new end @@ -42,8 +42,8 @@ def no_args # # @example # - # object.should_receive(:message).with(duck_type(:hello)) - # object.should_receive(:message).with(duck_type(:hello, :goodbye)) + # expect(object).to receive(:message).with(duck_type(:hello)) + # expect(object).to receive(:message).with(duck_type(:hello, :goodbye)) def duck_type(*args) DuckTypeMatcher.new(*args) end @@ -52,7 +52,7 @@ def duck_type(*args) # # @example # - # object.should_receive(:message).with(boolean()) + # expect(object).to receive(:message).with(boolean()) def boolean BooleanMatcher.new end @@ -62,9 +62,9 @@ def boolean # # @example # - # object.should_receive(:message).with(hash_including(:key => val)) - # object.should_receive(:message).with(hash_including(:key)) - # object.should_receive(:message).with(hash_including(:key, :key2 => val2)) + # expect(object).to receive(:message).with(hash_including(:key => val)) + # expect(object).to receive(:message).with(hash_including(:key)) + # expect(object).to receive(:message).with(hash_including(:key, :key2 => val2)) def hash_including(*args) HashIncludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args)) end @@ -74,8 +74,8 @@ def hash_including(*args) # # @example # - # object.should_receive(:message).with(array_including(1,2,3)) - # object.should_receive(:message).with(array_including([1,2,3])) + # expect(object).to receive(:message).with(array_including(1,2,3)) + # expect(object).to receive(:message).with(array_including([1,2,3])) def array_including(*args) actually_an_array = Array === args.first && args.count == 1 ? args.first : args ArrayIncludingMatcher.new(actually_an_array) @@ -85,9 +85,9 @@ def array_including(*args) # # @example # - # object.should_receive(:message).with(hash_excluding(:key => val)) - # object.should_receive(:message).with(hash_excluding(:key)) - # object.should_receive(:message).with(hash_excluding(:key, :key2 => :val2)) + # expect(object).to receive(:message).with(hash_excluding(:key => val)) + # expect(object).to receive(:message).with(hash_excluding(:key)) + # expect(object).to receive(:message).with(hash_excluding(:key, :key2 => :val2)) def hash_excluding(*args) HashExcludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args)) end @@ -98,7 +98,7 @@ def hash_excluding(*args) # # @example # - # object.should_receive(:message).with(instance_of(Thing)) + # expect(object).to receive(:message).with(instance_of(Thing)) def instance_of(klass) InstanceOf.new(klass) end @@ -108,7 +108,7 @@ def instance_of(klass) # Matches if `arg.kind_of?(klass)` # @example # - # object.should_receive(:message).with(kind_of(Thing)) + # expect(object).to receive(:message).with(kind_of(Thing)) def kind_of(klass) klass end diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 8e0878c90..2897d293b 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -39,6 +39,20 @@ def add_stub_and_should_receive_to(*modules) end end + # Provides the ability to set either `expect`, + # `should` or both syntaxes. RSpec uses `expect` + # syntax by default. This is needed if you want to + # explicitly enable `should` syntax and/or explicitly + # disable `expect` syntax. + # + # @example + # + # RSpec.configure do |rspec| + # rspec.mock_with :rspec do |mocks| + # mocks.syntax = [:expect, :should] + # end + # end + # def syntax=(*values) syntaxes = values.flatten if syntaxes.include?(:expect) @@ -54,6 +68,15 @@ def syntax=(*values) end end + # Returns an array with a list of syntaxes + # that are enabled. + # + # @example + # + # unless RSpec::Mocks.configuration.syntax.include?(:expect) + # raise "this RSpec extension gem requires the rspec-mocks `:expect` syntax" + # end + # def syntax syntaxes = [] syntaxes << :should if Syntax.should_enabled? diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 498097155..e449d22b7 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -92,11 +92,11 @@ def expected_args # # @example # - # counter.stub(:count).and_return(1) + # allow(counter).to receive(:count).and_return(1) # counter.count # => 1 # counter.count # => 1 # - # counter.stub(:count).and_return(1,2,3) + # allow(counter).to receive(:count).and_return(1,2,3) # counter.count # => 1 # counter.count # => 2 # counter.count # => 3 @@ -105,11 +105,11 @@ def expected_args # # etc # # # Supported, but ... - # counter.stub(:count).and_return { 1 } + # allow(counter).to receive(:count).and_return { 1 } # counter.count # => 1 # # # ... this is prefered - # counter.stub(:count) { 1 } + # allow(counter).to receive(:count) { 1 } # counter.count # => 1 def and_return(*values, &implementation) if negative? @@ -144,7 +144,7 @@ def yield_receiver_to_implementation_block? # # @example # - # counter.should_receive(:increment).and_call_original + # expect(counter).to receive(:increment).and_call_original # original_count = counter.count # counter.increment # expect(counter.count).to eq(original_count + 1) @@ -175,10 +175,10 @@ def and_call_original # # @example # - # car.stub(:go).and_raise - # car.stub(:go).and_raise(OutOfGas) - # car.stub(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive") - # car.stub(:go).and_raise(OutOfGas.new(2, :oz)) + # allow(car).to receive(:go).and_raise + # allow(car).to receive(:go).and_raise(OutOfGas) + # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive") + # allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz)) def and_raise(exception = RuntimeError, message = nil) if exception.respond_to?(:exception) exception = message ? exception.exception(message) : exception.exception @@ -196,8 +196,8 @@ def and_raise(exception = RuntimeError, message = nil) # # @example # - # car.stub(:go).and_throw(:out_of_gas) - # car.stub(:go).and_throw(:out_of_gas, :level => 0.1) + # allow(car).to receive(:go).and_throw(:out_of_gas) + # allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1) def and_throw(*args) self.terminal_implementation_action = Proc.new { throw(*args) } nil @@ -332,14 +332,14 @@ def raise_out_of_order_error # # @example # - # cart.stub(:add) { :failure } - # cart.stub(:add).with(Book.new(:isbn => 1934356379)) { :success } + # allow(cart).to receive(:add) { :failure } + # allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } # cart.add(Book.new(:isbn => 1234567890)) # # => :failure # cart.add(Book.new(:isbn => 1934356379)) # # => :success # - # cart.should_receive(:add).with(Book.new(:isbn => 1934356379)) { :success } + # expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } # cart.add(Book.new(:isbn => 1234567890)) # # => failed expectation # cart.add(Book.new(:isbn => 1934356379)) @@ -360,7 +360,7 @@ def with(*args, &block) # # @example # - # dealer.should_receive(:deal_card).exactly(10).times + # expect(dealer).to receive(:deal_card).exactly(10).times def exactly(n, &block) self.inner_implementation_action = block set_expected_received_count :exactly, n @@ -372,7 +372,7 @@ def exactly(n, &block) # # @example # - # dealer.should_receive(:deal_card).at_least(9).times + # expect(dealer).to receive(:deal_card).at_least(9).times def at_least(n, &block) set_expected_received_count :at_least, n @@ -390,7 +390,7 @@ def at_least(n, &block) # # @example # - # dealer.should_receive(:deal_card).at_most(10).times + # expect(dealer).to receive(:deal_card).at_most(10).times def at_most(n, &block) self.inner_implementation_action = block set_expected_received_count :at_most, n @@ -401,9 +401,9 @@ def at_most(n, &block) # # @example # - # dealer.should_receive(:deal_card).exactly(10).times - # dealer.should_receive(:deal_card).at_least(10).times - # dealer.should_receive(:deal_card).at_most(10).times + # expect(dealer).to receive(:deal_card).exactly(10).times + # expect(dealer).to receive(:deal_card).at_least(10).times + # expect(dealer).to receive(:deal_card).at_most(10).times def times(&block) self.inner_implementation_action = block self @@ -413,7 +413,7 @@ def times(&block) # # @example # - # car.should_receive(:stop).never + # expect(car).to receive(:stop).never def never ErrorGenerator.raise_double_negation_error("expect(obj)") if negative? @expected_received_count = 0 @@ -424,7 +424,7 @@ def never # # @example # - # car.should_receive(:go).once + # expect(car).to receive(:go).once def once(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 1 @@ -435,7 +435,7 @@ def once(&block) # # @example # - # car.should_receive(:go).twice + # expect(car).to receive(:go).twice def twice(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 2 @@ -446,9 +446,9 @@ def twice(&block) # # @example # - # api.should_receive(:prepare).ordered - # api.should_receive(:run).ordered - # api.should_receive(:finish).ordered + # expect(api).to receive(:prepare).ordered + # expect(api).to receive(:run).ordered + # expect(api).to receive(:finish).ordered def ordered(&block) self.inner_implementation_action = block @ordered = true From 0c9fb7aa317400a9e74917aafa7a4b7994d6590f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 20 Dec 2013 12:33:29 -0800 Subject: [PATCH 0380/1179] Update 2.1 to rc1. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7bf36ef1e..480258d62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ rvm: - 1.9.2 - 1.9.3 - 2.0.0 - - 2.1.0-preview2 + - 2.1.0-rc1 - ree - jruby-18mode - jruby-19mode From 2081e74029967da82273d05a5f7c02d619346110 Mon Sep 17 00:00:00 2001 From: Arthur Nogueira Neves Date: Sat, 28 Dec 2013 14:09:37 -0500 Subject: [PATCH 0381/1179] travis config: Use latest 2.1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 480258d62..b1801b1e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ rvm: - 1.9.2 - 1.9.3 - 2.0.0 - - 2.1.0-rc1 + - 2.1.0 - ree - jruby-18mode - jruby-19mode From 4b9d6b7e1a2adc89cea5b445f76bd49538111932 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Mon, 30 Dec 2013 23:45:14 +0000 Subject: [PATCH 0382/1179] Add specs demonstrating how args work with receive_message_chain --- spec/rspec/mocks/matchers/receive_message_chain_spec.rb | 8 ++++++++ spec/rspec/mocks/stub_chain_spec.rb | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index 0d2eac20c..106bfe0fa 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -102,6 +102,14 @@ module RSpec::Mocks::Matchers expect(object.to_a.length).to eq(3) end + it "accepts any number of arguments to the stubbed messages in the chain" do + allow(object).to receive_message_chain(:msg1, :msg2).and_return(:return_value) + + expect { + object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value") + }.not_to raise_error + end + it "raises when expect is used and the entire chain isn't called" do expect { expect(object).to receive_message_chain(:to_a, :farce, :length => 3) diff --git a/spec/rspec/mocks/stub_chain_spec.rb b/spec/rspec/mocks/stub_chain_spec.rb index c617bcb70..15ff9bcd6 100644 --- a/spec/rspec/mocks/stub_chain_spec.rb +++ b/spec/rspec/mocks/stub_chain_spec.rb @@ -37,6 +37,14 @@ module Mocks end context "with two methods in chain" do + it "accepts any number of arguments to the stubbed messages in the chain" do + object.stub_chain(:msg1, :msg2).and_return(:return_value) + + expect { + object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value") + }.not_to raise_error + end + context "using and_return" do it "returns expected value from chaining two method calls" do object.stub_chain(:msg1, :msg2).and_return(:return_value) From 30b1648440b8954f3275054dc6351eedce5e29c5 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 31 Dec 2013 00:02:41 +0000 Subject: [PATCH 0383/1179] Fix inline hash return with args in message_chain Closes #509 --- lib/rspec/mocks/message_chain.rb | 2 +- .../rspec/mocks/matchers/receive_message_chain_spec.rb | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/message_chain.rb b/lib/rspec/mocks/message_chain.rb index 97f2762a1..60d2bfbbb 100644 --- a/lib/rspec/mocks/message_chain.rb +++ b/lib/rspec/mocks/message_chain.rb @@ -44,7 +44,7 @@ def format_chain(*chain, &blk) hash = chain.pop hash.each do |k,v| chain << k - blk = lambda { v } + blk = lambda { |*_| v } end end return chain.join('.').split('.'), blk diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index 106bfe0fa..a9670cd64 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -102,7 +102,7 @@ module RSpec::Mocks::Matchers expect(object.to_a.length).to eq(3) end - it "accepts any number of arguments to the stubbed messages in the chain" do + it "accepts any number of arguments to the stubbed messages" do allow(object).to receive_message_chain(:msg1, :msg2).and_return(:return_value) expect { @@ -110,6 +110,14 @@ module RSpec::Mocks::Matchers }.not_to raise_error end + it "accepts any number of arguments to the stubbed messages with an inline hash return value" do + allow(object).to receive_message_chain(:msg1, :msg2 => :return_value) + + expect { + object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value") + }.not_to raise_error + end + it "raises when expect is used and the entire chain isn't called" do expect { expect(object).to receive_message_chain(:to_a, :farce, :length => 3) From af83c30915fcd74c06cd98d399080e6596ced956 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 30 Dec 2013 21:13:15 -0800 Subject: [PATCH 0384/1179] Add temporary workaround for failing build on Ruby 1.8.7 Rubygems broke builds on Ruby 1.8.7: travis-ci/travis-ci#1793 rubygems/rubygems#763 This patch is a temporary workaround until rubygems is updated. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index b1801b1e8..896e7c7aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,4 +11,9 @@ rvm: - jruby-19mode - rbx-2.2.1 - rbx +before_install: + - gem update bundler + - bundle --version + - gem update --system 2.1.11 + - gem --version From e5bbdfd0624466a02af0b3825a1c0f466dd2c61f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 30 Dec 2013 21:32:23 -0800 Subject: [PATCH 0385/1179] Just build against latest rbx release. Our build is broken against 2.2.1: rubinius/rubinius#2855 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 896e7c7aa..e8cbfd8bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ rvm: - ree - jruby-18mode - jruby-19mode - - rbx-2.2.1 - rbx before_install: - gem update bundler From f888eee6f96fdc1a1b5c41391cb79c29cf655681 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 31 Dec 2013 16:42:42 +0000 Subject: [PATCH 0386/1179] USe a proc on message_chain as it takes any args --- lib/rspec/mocks/message_chain.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/message_chain.rb b/lib/rspec/mocks/message_chain.rb index 60d2bfbbb..a46f87de6 100644 --- a/lib/rspec/mocks/message_chain.rb +++ b/lib/rspec/mocks/message_chain.rb @@ -44,7 +44,7 @@ def format_chain(*chain, &blk) hash = chain.pop hash.each do |k,v| chain << k - blk = lambda { |*_| v } + blk = Proc.new { v } end end return chain.join('.').split('.'), blk From e03c1214533fa90c56a64cb69036972c6a14c233 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 31 Dec 2013 16:42:53 +0000 Subject: [PATCH 0387/1179] Improve the argument related message_chain specs --- spec/rspec/mocks/matchers/receive_message_chain_spec.rb | 8 ++------ spec/rspec/mocks/stub_chain_spec.rb | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index a9670cd64..08ad7ac81 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -105,17 +105,13 @@ module RSpec::Mocks::Matchers it "accepts any number of arguments to the stubbed messages" do allow(object).to receive_message_chain(:msg1, :msg2).and_return(:return_value) - expect { - object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value") - }.not_to raise_error + expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value) end it "accepts any number of arguments to the stubbed messages with an inline hash return value" do allow(object).to receive_message_chain(:msg1, :msg2 => :return_value) - expect { - object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value") - }.not_to raise_error + expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value) end it "raises when expect is used and the entire chain isn't called" do diff --git a/spec/rspec/mocks/stub_chain_spec.rb b/spec/rspec/mocks/stub_chain_spec.rb index 15ff9bcd6..a566e5abb 100644 --- a/spec/rspec/mocks/stub_chain_spec.rb +++ b/spec/rspec/mocks/stub_chain_spec.rb @@ -40,9 +40,7 @@ module Mocks it "accepts any number of arguments to the stubbed messages in the chain" do object.stub_chain(:msg1, :msg2).and_return(:return_value) - expect { - object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value") - }.not_to raise_error + expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value) end context "using and_return" do From eb8bbba5ce840e5df5287faf62540477e8e05d72 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 31 Dec 2013 17:17:45 -0800 Subject: [PATCH 0388/1179] Refactor: leverage rspec-support's fuzzy matching. --- lib/rspec/mocks/argument_list_matcher.rb | 33 +++++------------------- lib/rspec/mocks/argument_matchers.rb | 15 +---------- 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index e9a018217..e9b8e99d2 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -1,4 +1,5 @@ require 'rspec/mocks/argument_matchers' +require 'rspec/support/fuzzy_matcher' module RSpec module Mocks @@ -35,23 +36,17 @@ class ArgumentListMatcher # @param [Block] block a block with arity matching the expected # # Initializes an `ArgumentListMatcher` with a collection of literal - # values and/or argument matchers, or a block that handles the evaluation - # for you. + # values and/or argument matchers. # # @see ArgumentMatchers # @see #args_match? def initialize(*expected_args) @expected_args = expected_args - @match_any_args = false - @matchers = nil - case expected_args.first - when ArgumentMatchers::AnyArgsMatcher - @match_any_args = true - when ArgumentMatchers::NoArgsMatcher - @matchers = [] - else - @matchers = expected_args + @matchers = case expected_args.first + when ArgumentMatchers::AnyArgsMatcher then Array + when ArgumentMatchers::NoArgsMatcher then [] + else expected_args end end @@ -63,21 +58,7 @@ def initialize(*expected_args) # # @see #initialize def args_match?(*args) - match_any_args? || matchers_match?(*args) - end - - private - - def matchers_match?(*args) - return false unless @matchers.count == args.count - - @matchers.zip(args).all? do |(matcher, arg)| - ArgumentMatchers.values_match?(matcher, arg) - end - end - - def match_any_args? - @match_any_args + Support::FuzzyMatcher.values_match?(@matchers, args) end # Value that will match all argument lists. diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 9372f2967..95006afa9 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -122,19 +122,6 @@ def self.anythingize_lonely_keys(*args) hash end - # @api private - # Implements our matching semantics for two arbitrary values. - def self.values_match?(expected, actual) - # `===` provides the main matching semantics we want, but - # has some slight gotchas: - # - # * `Fixnum === Fixnum` returns false. - # * `/abc/ === /abc/` returns false. - # - # So, for cases like these, we check `==` as well as a fallback. - expected === actual || actual == expected - end - # @api private class AnyArgsMatcher def description @@ -171,7 +158,7 @@ def initialize(expected) def ===(predicate, actual) @expected.__send__(predicate) do |k, v| - actual.has_key?(k) && ArgumentMatchers.values_match?(v, actual[k]) + actual.has_key?(k) && Support::FuzzyMatcher.values_match?(v, actual[k]) end rescue NoMethodError false From b33e36dbecb0fcb1ff2d3489ef2f97c23670090b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 31 Dec 2013 23:11:53 -0800 Subject: [PATCH 0389/1179] Use `method_handle_for` from rspec-support. --- lib/rspec/mocks.rb | 20 +------------------- lib/rspec/mocks/any_instance/recorder.rb | 4 ++-- lib/rspec/mocks/instance_method_stasher.rb | 2 +- lib/rspec/mocks/proxy.rb | 2 +- 4 files changed, 5 insertions(+), 23 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index b6217e9ab..03a6dbb01 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -1,5 +1,6 @@ require 'rspec/mocks/framework' require 'rspec/mocks/version' +require 'rspec/support' module RSpec # Contains top-level utility methods. While this contains a few @@ -91,25 +92,6 @@ def self.any_instance_recorder_for(klass) space.any_instance_recorder_for(klass) end - # @api private - KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method) - - # @api private - # Used internally to get a method handle for a particular object - # and method name. - # - # Includes handling for a few special cases: - # - # - Objects that redefine #method (e.g. an HTTPRequest struct) - # - BasicObject subclasses that mixin a Kernel dup (e.g. SimpleDelegator) - def self.method_handle_for(object, method_name) - if ::Kernel === object - KERNEL_METHOD_METHOD.bind(object).call(method_name) - else - object.method(method_name) - end - end - # @private IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored' end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 140ec18b3..e3f63977e 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -212,7 +212,7 @@ def observe!(method_name) @observed_methods << method_name backup_method!(method_name) @klass.__send__(:define_method, method_name) do |*args, &blk| - klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner + klass = ::RSpec::Support.method_handle_for(self, method_name).owner ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name) self.__send__(method_name, *args, &blk) end @@ -221,7 +221,7 @@ def observe!(method_name) def mark_invoked!(method_name) backup_method!(method_name) @klass.__send__(:define_method, method_name) do |*args, &blk| - klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner + klass = ::RSpec::Support.method_handle_for(self, method_name).owner invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name) raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{self.inspect} but has already been received by #{invoked_instance}" end diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index ef295fa81..fc4d6f742 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -54,7 +54,7 @@ def method_is_stashed? # @private def stash return if !method_defined_directly_on_klass? - @original_method ||= ::RSpec::Mocks.method_handle_for(@object, @method) + @original_method ||= ::RSpec::Support.method_handle_for(@object, @method) end # @private diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 577b055e8..4854c881f 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -246,7 +246,7 @@ def method_handle_for(message) build_alias_method_name(message) end - ::RSpec::Mocks.method_handle_for(@object, message) + ::RSpec::Support.method_handle_for(@object, message) rescue NameError nil end From 1e15b6ed0e50f74910dccefd37d0982c585311b3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 1 Jan 2014 00:08:41 -0800 Subject: [PATCH 0390/1179] Add changelog for #508. [ci skip] --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 70da1c27b..03c878d20 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,6 +31,9 @@ Bug Fixes: you are not actually doing that. (Myron Marston) * Fix `any_instance.unstub` when used on sub classes for whom the super class has had `any_instance.stub` invoked on. (Jon Rowe) +* Fix regression in `stub_chain`/`receive_message_chain` that caused + it to raise an `ArgumentError` when passing args to the stubbed + methods. (Sam Phippen) Enhancements: From b88a050b77adeb3bdcfc28c7509d58089607edbe Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sat, 2 Nov 2013 15:08:35 +0000 Subject: [PATCH 0391/1179] Prevent doubles and partial doubles being used outside of tests. For example: in a before(:all) block. --- Changelog.md | 2 + features/test_frameworks/test_unit.feature | 4 + lib/rspec/mocks.rb | 11 +- lib/rspec/mocks/error_space.rb | 31 ++++++ lib/rspec/mocks/matchers/have_received.rb | 8 +- lib/rspec/mocks/standalone.rb | 1 + spec/rspec/mocks/null_object_mock_spec.rb | 10 +- .../mocks/stubbing_in_before_all_spec.rb | 103 ++++++++++++++++++ spec/rspec/mocks_spec.rb | 4 +- 9 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 lib/rspec/mocks/error_space.rb create mode 100644 spec/rspec/mocks/stubbing_in_before_all_spec.rb diff --git a/Changelog.md b/Changelog.md index 03c878d20..e2894bfcb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,8 @@ Breaking Changes for 3.0.0: * Change how to integrate rspec-mocks in other test frameworks. You now need to include `RSpec::Mocks::ExampleMethods` in your test context. (Myron Marston) +* Prevent RSpec mocks' doubles and partial doubles from being used outside of + the per-test lifecycle (e.g. from a `before(:all)` hook). (Sam Phippen) Bug Fixes: diff --git a/features/test_frameworks/test_unit.feature b/features/test_frameworks/test_unit.feature index 366603053..d60306993 100644 --- a/features/test_frameworks/test_unit.feature +++ b/features/test_frameworks/test_unit.feature @@ -15,6 +15,10 @@ Feature: Test::Unit integration class RSpecMocksTest < Test::Unit::TestCase include RSpec::Mocks::ExampleMethods + def setup + RSpec::Mocks.setup + end + def teardown RSpec::Mocks.verify ensure diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 03a6dbb01..81139f980 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -1,6 +1,7 @@ require 'rspec/mocks/framework' require 'rspec/mocks/version' require 'rspec/support' +require "rspec/mocks/error_space" module RSpec # Contains top-level utility methods. While this contains a few @@ -8,18 +9,21 @@ module RSpec # a test or example. They exist primarily for integration with # test frameworks (such as rspec-core). module Mocks + ERROR_SPACE = RSpec::Mocks::ErrorSpace.new + MOCK_SPACE = RSpec::Mocks::Space.new + class << self # Stores rspec-mocks' global state. # @api private attr_accessor :space end - self.space = RSpec::Mocks::Space.new + self.space = ERROR_SPACE # Performs per-test/example setup. This should be called before # an test or example begins. - def self.setup(host) - # Nothing to do for now + def self.setup(host=nil) + self.space = MOCK_SPACE end # Verifies any message expectations that were set during the @@ -33,6 +37,7 @@ def self.verify # each example, even if an error was raised during the example. def self.teardown space.reset_all + self.space = ERROR_SPACE end # Adds an allowance (stub) on `subject` diff --git a/lib/rspec/mocks/error_space.rb b/lib/rspec/mocks/error_space.rb new file mode 100644 index 000000000..aefddb049 --- /dev/null +++ b/lib/rspec/mocks/error_space.rb @@ -0,0 +1,31 @@ +module RSpec + module Mocks + # @private + # Raised when doubles or partial doubles are used outside of the per-test lifecycle. + OutsideOfExampleError = Class.new(StandardError) + + # @private + class ErrorSpace + def proxy_for(*args) + raise_lifecycle_message + end + + def any_instance_recorder_for(*args) + raise_lifecycle_message + end + + def reset_all + end + + def verify_all + end + + private + + def raise_lifecycle_message + raise OutsideOfExampleError, "The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported." + end + end + + end +end diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index c1743a322..fe801d55f 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -54,9 +54,11 @@ def description private def expect - expectation = mock_proxy.build_expectation(@method_name) - apply_constraints_to expectation - expectation + @expectation ||= begin + expectation = mock_proxy.build_expectation(@method_name) + apply_constraints_to expectation + expectation + end end def apply_constraints_to(expectation) diff --git a/lib/rspec/mocks/standalone.rb b/lib/rspec/mocks/standalone.rb index f9330fbe7..c586712a1 100644 --- a/lib/rspec/mocks/standalone.rb +++ b/lib/rspec/mocks/standalone.rb @@ -1,2 +1,3 @@ require 'rspec/mocks' include RSpec::Mocks::ExampleMethods +RSpec::Mocks.setup diff --git a/spec/rspec/mocks/null_object_mock_spec.rb b/spec/rspec/mocks/null_object_mock_spec.rb index b67b3c686..e6b571a53 100644 --- a/spec/rspec/mocks/null_object_mock_spec.rb +++ b/spec/rspec/mocks/null_object_mock_spec.rb @@ -89,15 +89,11 @@ module Mocks expect(("%i" % @double)).to eq("0") end - it "does not allow null-ness to persist between examples" do + it "does not allow null objects to be used outside of examples" do RSpec::Mocks.teardown - expect(@double).not_to be_null_object - expect { @double.some.long.message.chain }.to raise_error(RSpec::Mocks::MockExpectationError) - - @double.as_null_object - expect(@double).to be_null_object - expect { @double.some.long.message.chain }.not_to raise_error + expect { @double.some.long.message.chain }.to raise_error(RSpec::Mocks::OutsideOfExampleError) + expect { @double.as_null_object }.to raise_error(RSpec::Mocks::OutsideOfExampleError) end end diff --git a/spec/rspec/mocks/stubbing_in_before_all_spec.rb b/spec/rspec/mocks/stubbing_in_before_all_spec.rb new file mode 100644 index 000000000..216e5ab95 --- /dev/null +++ b/spec/rspec/mocks/stubbing_in_before_all_spec.rb @@ -0,0 +1,103 @@ +require "spec_helper" +require "delegate" + +describe "Stubbing/mocking methods in before(:all) blocks" do + old_rspec = nil + + shared_examples_for "A stub/mock in a before(:all) block" do |message_expectation_block| + the_error = nil + before(:all) do + begin + use_rspec_mocks + rescue + the_error = $! + end + end + + it "raises an error with a useful message" do + expect(the_error).to be_a_kind_of(RSpec::Mocks::OutsideOfExampleError) + + expect(the_error.message).to match(/The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported./) + end + end + + describe "#stub" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + Object.stub(:foo) + end + end + end + + describe "#unstub" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + Object.unstub(:foo) + end + end + end + + describe "#should_receive" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + Object.should_receive(:foo) + end + end + end + + describe "#should_not_receive" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + Object.should_not_receive(:foo) + end + end + end + + describe "#any_instance" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + Object.any_instance.should_receive(:foo) + end + end + end + + describe "#stub_chain" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + Object.stub_chain(:foo) + end + end + end + + describe "#expect(...).to receive" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + expect(Object).to receive(:foo) + end + end + end + + describe "#allow(...).to receive" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + allow(Object).to receive(:foo) + end + end + end + + describe "#expect_any_instance_of(...).to receive" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + expect_any_instance_of(Object).to receive(:foo) + end + end + end + + describe "#allow_any_instance_of(...).to receive" do + it_behaves_like "A stub/mock in a before(:all) block" do + def use_rspec_mocks + allow_any_instance_of(Object).to receive(:foo) + end + end + end +end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 2d5531941..f54834609 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -12,13 +12,13 @@ end describe "::teardown" do - it "delegates to the space" do + it "prevents further expectations from being set" do foo = double foo.should_receive(:bar) RSpec::Mocks::teardown expect do foo.bar - end.to raise_error(/received unexpected message/) + end.to raise_error(RSpec::Mocks::OutsideOfExampleError) end end From b69dce857ec03fd1c20c1736b166f362086004de Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 2 Jan 2014 20:09:09 +0000 Subject: [PATCH 0392/1179] Remove the host argument from RSpec::Mocks.setup --- lib/rspec/mocks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 81139f980..f0eefb9bd 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -22,7 +22,7 @@ class << self # Performs per-test/example setup. This should be called before # an test or example begins. - def self.setup(host=nil) + def self.setup self.space = MOCK_SPACE end From 66dcbdaec6ae1b93e21ff53d0feb37b7f590c1d6 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 3 Jan 2014 00:08:45 +0000 Subject: [PATCH 0393/1179] Add a changelog entry for #514 --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index e2894bfcb..7d03d6634 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,9 @@ Breaking Changes for 3.0.0: (Myron Marston) * Prevent RSpec mocks' doubles and partial doubles from being used outside of the per-test lifecycle (e.g. from a `before(:all)` hook). (Sam Phippen) +* Remove the `host` argument of `RSpec::Mocks.setup`. Instead + `RSpec::Mocks::ExampleMethods` should be included directly in the scope where + RSpec's mocking capabilities are used. (Sam Phippen) Bug Fixes: From 7a0820aff8f19f31547c7f128ad06164fa2607a7 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 3 Jan 2014 00:34:42 +0000 Subject: [PATCH 0394/1179] Fix a warning introduced with some orderings on the before(:all) specs --- spec/rspec/mocks/stubbing_in_before_all_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/stubbing_in_before_all_spec.rb b/spec/rspec/mocks/stubbing_in_before_all_spec.rb index 216e5ab95..5086da133 100644 --- a/spec/rspec/mocks/stubbing_in_before_all_spec.rb +++ b/spec/rspec/mocks/stubbing_in_before_all_spec.rb @@ -4,7 +4,7 @@ describe "Stubbing/mocking methods in before(:all) blocks" do old_rspec = nil - shared_examples_for "A stub/mock in a before(:all) block" do |message_expectation_block| + shared_examples_for "A stub/mock in a before(:all) block" do the_error = nil before(:all) do begin From 30ee1124e101b0de4c944f5e2dc53fd7c3487c28 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 3 Jan 2014 01:17:57 +0000 Subject: [PATCH 0395/1179] Protect from fixnum to array casting on 1.8.7 in a failing spec. --- spec/rspec/mocks/failing_argument_matchers_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/rspec/mocks/failing_argument_matchers_spec.rb b/spec/rspec/mocks/failing_argument_matchers_spec.rb index 014085ea6..596dcc7ce 100644 --- a/spec/rspec/mocks/failing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/failing_argument_matchers_spec.rb @@ -63,9 +63,11 @@ module Mocks end it "matches against a Matcher" do + # This spec is generating warnings on 1.8.7, not sure why so + # this does with_isolated_stderr to kill them. @samphippen 3rd Jan 2013. expect do @double.should_receive(:msg).with(equal(3)) - @double.msg(37) + with_isolated_stderr { @double.msg(37) } end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (equal 3)\n got: (37)") end From 5bd9a093deec52e24a7f3c1b60e26c51dae7401b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 2 Jan 2014 21:11:36 -0800 Subject: [PATCH 0396/1179] Allow objects to be marshaled outside per-test lifecycle. This was reported by @noelrappin: https://twitter.com/noelrap/status/418853517323796480 --- lib/rspec/mocks/error_space.rb | 4 ++++ spec/rspec/mocks/extensions/marshal_spec.rb | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/rspec/mocks/error_space.rb b/lib/rspec/mocks/error_space.rb index aefddb049..599065749 100644 --- a/lib/rspec/mocks/error_space.rb +++ b/lib/rspec/mocks/error_space.rb @@ -20,6 +20,10 @@ def reset_all def verify_all end + def registered?(object) + false + end + private def raise_lifecycle_message diff --git a/spec/rspec/mocks/extensions/marshal_spec.rb b/spec/rspec/mocks/extensions/marshal_spec.rb index 46f436b34..b24f2e097 100644 --- a/spec/rspec/mocks/extensions/marshal_spec.rb +++ b/spec/rspec/mocks/extensions/marshal_spec.rb @@ -50,5 +50,22 @@ def without_space expect(Marshal.load(serialized)).to be_nil end end + + context 'outside the per-test lifecycle' do + def outside_per_test_lifecycle + RSpec::Mocks.teardown + yield + ensure + RSpec::Mocks.setup + end + + it 'does not duplicate the object before serialization' do + obj = UndupableObject.new + outside_per_test_lifecycle do + serialized = Marshal.dump(obj) + expect(Marshal.load(serialized)).to be_an(UndupableObject) + end + end + end end end From d6c3e525a3d5bb9c35737c4e6be8c4a81c95afa4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 2 Jan 2014 21:34:10 -0800 Subject: [PATCH 0397/1179] Remove some config that's been moved into rspec-support. --- spec/spec_helper.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index be0c61486..d0e6a1b96 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -43,8 +43,6 @@ def prevents(&block) config.mock_with :rspec config.color_enabled = true config.order = :random - config.run_all_when_everything_filtered = true - config.filter_run_including :focus config.expect_with :rspec do |expectations| expectations.syntax = :expect From 325139bf4a323adc393e0a913c41bc545da8785d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 2 Jan 2014 22:34:03 -0800 Subject: [PATCH 0398/1179] Move these specs under the `RSpec::Mocks::Double` example group. These were defined at the top level with descriptions like `==` or `with`, which made no sense without the context of `RSpec::Mocks::Double`. --- spec/rspec/mocks/double_spec.rb | 241 ++++++++++++++++---------------- 1 file changed, 120 insertions(+), 121 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 409604cfe..09707f132 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -659,169 +659,168 @@ def initialize(amount, units) expect(double.message).to eq :response end - end - - describe "a double message receiving a block" do - before(:each) do - @double = double("double") - @calls = 0 - end + describe "a double message receiving a block" do + before(:each) do + @double = double("double") + @calls = 0 + end - def add_call - @calls = @calls + 1 - end + def add_call + @calls = @calls + 1 + end - it "calls the block after #should_receive" do - @double.should_receive(:foo) { add_call } + it "calls the block after #should_receive" do + @double.should_receive(:foo) { add_call } - @double.foo + @double.foo - expect(@calls).to eq 1 - end + expect(@calls).to eq 1 + end - it "calls the block after #should_receive after a similar stub" do - @double.stub(:foo).and_return(:bar) - @double.should_receive(:foo) { add_call } + it "calls the block after #should_receive after a similar stub" do + @double.stub(:foo).and_return(:bar) + @double.should_receive(:foo) { add_call } - @double.foo + @double.foo - expect(@calls).to eq 1 - end + expect(@calls).to eq 1 + end - it "calls the block after #once" do - @double.should_receive(:foo).once { add_call } + it "calls the block after #once" do + @double.should_receive(:foo).once { add_call } - @double.foo + @double.foo - expect(@calls).to eq 1 - end + expect(@calls).to eq 1 + end - it "calls the block after #twice" do - @double.should_receive(:foo).twice { add_call } + it "calls the block after #twice" do + @double.should_receive(:foo).twice { add_call } - @double.foo - @double.foo + @double.foo + @double.foo - expect(@calls).to eq 2 - end + expect(@calls).to eq 2 + end - it "calls the block after #times" do - @double.should_receive(:foo).exactly(10).times { add_call } + it "calls the block after #times" do + @double.should_receive(:foo).exactly(10).times { add_call } - (1..10).each { @double.foo } + (1..10).each { @double.foo } - expect(@calls).to eq 10 - end + expect(@calls).to eq 10 + end - it "calls the block after #ordered" do - @double.should_receive(:foo).ordered { add_call } - @double.should_receive(:bar).ordered { add_call } + it "calls the block after #ordered" do + @double.should_receive(:foo).ordered { add_call } + @double.should_receive(:bar).ordered { add_call } - @double.foo - @double.bar + @double.foo + @double.bar - expect(@calls).to eq 2 + expect(@calls).to eq 2 + end end - end - describe 'string representation generated by #to_s' do - it 'does not contain < because that might lead to invalid HTML in some situations' do - double = double("Dog") - valid_html_str = "#{double}" - expect(valid_html_str).not_to include('<') + describe 'string representation generated by #to_s' do + it 'does not contain < because that might lead to invalid HTML in some situations' do + double = double("Dog") + valid_html_str = "#{double}" + expect(valid_html_str).not_to include('<') + end end - end - describe "string representation generated by #to_str" do - it "looks the same as #to_s" do - double = double("Foo") - expect(double.to_str).to eq double.to_s + describe "string representation generated by #to_str" do + it "looks the same as #to_s" do + double = double("Foo") + expect(double.to_str).to eq double.to_s + end end - end - describe "double created with no name" do - it "does not use a name in a failure message" do - double = double() - expect {double.foo}.to raise_error(/Double received/) - end + describe "double created with no name" do + it "does not use a name in a failure message" do + double = double() + expect {double.foo}.to raise_error(/Double received/) + end - it "does respond to initially stubbed methods" do - double = double(:foo => "woo", :bar => "car") - expect(double.foo).to eq "woo" - expect(double.bar).to eq "car" + it "does respond to initially stubbed methods" do + double = double(:foo => "woo", :bar => "car") + expect(double.foo).to eq "woo" + expect(double.bar).to eq "car" + end end - end - describe "==" do - it "sends '== self' to the comparison object" do - first = double('first') - second = double('second') + describe "==" do + it "sends '== self' to the comparison object" do + first = double('first') + second = double('second') - first.should_receive(:==).with(second) - second == first + first.should_receive(:==).with(second) + second == first + end end - end - describe "with" do - before { @double = double('double') } - context "with args" do - context "with matching args" do - it "passes" do - @double.should_receive(:foo).with('bar') - @double.foo('bar') + describe "with" do + before { @double = double('double') } + context "with args" do + context "with matching args" do + it "passes" do + @double.should_receive(:foo).with('bar') + @double.foo('bar') + end end - end - context "with non-matching args" do - it "fails" do - @double.should_receive(:foo).with('bar') - expect do - @double.foo('baz') - end.to raise_error - reset @double + context "with non-matching args" do + it "fails" do + @double.should_receive(:foo).with('bar') + expect do + @double.foo('baz') + end.to raise_error + reset @double + end end - end - context "with non-matching doubles" do - it "fails" do - d1 = double('1') - d2 = double('2') - @double.should_receive(:foo).with(d1) - expect do - @double.foo(d2) - end.to raise_error - reset @double + context "with non-matching doubles" do + it "fails" do + d1 = double('1') + d2 = double('2') + @double.should_receive(:foo).with(d1) + expect do + @double.foo(d2) + end.to raise_error + reset @double + end end - end - context "with non-matching doubles as_null_object" do - it "fails" do - d1 = double('1').as_null_object - d2 = double('2').as_null_object - @double.should_receive(:foo).with(d1) - expect do - @double.foo(d2) - end.to raise_error - reset @double + context "with non-matching doubles as_null_object" do + it "fails" do + d1 = double('1').as_null_object + d2 = double('2').as_null_object + @double.should_receive(:foo).with(d1) + expect do + @double.foo(d2) + end.to raise_error + reset @double + end end end - end - context "with a block" do - context "with matching args" do - it "returns the result of the block" do - @double.should_receive(:foo).with('bar') { 'baz' } - expect(@double.foo('bar')).to eq('baz') + context "with a block" do + context "with matching args" do + it "returns the result of the block" do + @double.should_receive(:foo).with('bar') { 'baz' } + expect(@double.foo('bar')).to eq('baz') + end end - end - context "with non-matching args" do - it "fails" do - @double.should_receive(:foo).with('bar') { 'baz' } - expect do - expect(@double.foo('wrong')).to eq('baz') - end.to raise_error(/received :foo with unexpected arguments/) - reset @double + context "with non-matching args" do + it "fails" do + @double.should_receive(:foo).with('bar') { 'baz' } + expect do + expect(@double.foo('wrong')).to eq('baz') + end.to raise_error(/received :foo with unexpected arguments/) + reset @double + end end end end From c43934991f11257216e524c9e6d3899175fb76e8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 16:48:40 -0800 Subject: [PATCH 0399/1179] Stop double-resetting mock proxies. We don't need to reset the mock proxy while verifying it. That'll happen later during the teardown phase. It looks like the main reason we were doing this was to assist with rspec-mocks testing itself; we would verify the middle of an example to assert a mock expectation failure, and a `reset` is needed so it doesn't fail again when rspec-core calls `verify` as well. The solution is to use spec helper methods that perform the reset when we verify in the middle of an example. I don't have any evidence this improves perf, but it seems reasonable to assume that removing an extra method call per example can only make it faster (however slight the improvement may be). Fixes #165. --- lib/rspec/mocks/proxy.rb | 2 -- spec/rspec/mocks/any_instance_spec.rb | 30 +++++++++---------- .../mocks/matchers/receive_messages_spec.rb | 4 +-- spec/rspec/mocks/matchers/receive_spec.rb | 6 +--- spec/rspec/mocks_spec.rb | 2 ++ spec/spec_helper.rb | 11 ++++++- 6 files changed, 30 insertions(+), 25 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 4854c881f..a9d6899a9 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -124,8 +124,6 @@ def remove_single_stub(method_name, stub) # @private def verify @method_doubles.each_value {|d| d.verify} - ensure - reset end # @private diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 1e464afc7..e687397a3 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -252,7 +252,7 @@ def private_method; :private_method_return_value; end # ::RSpec::Mocks.any_instance_recorder_for(klass).stop_all_observation! # but that is never invoked in :: expect { - RSpec::Mocks.space.verify_all + verify_all }.to( change { obj.existing_method }.from("stubbed value").to(:existing_method_return_value) ) @@ -392,7 +392,7 @@ class RSpec::SampleRspecTestClass;end klass.any_instance.should_receive(:foo) klass.any_instance.should_not_receive(:bar) klass.new.foo - RSpec::Mocks.space.verify_all + verify_all end end @@ -417,14 +417,14 @@ class RSpec::SampleRspecTestClass;end expect do klass.any_instance.should_receive(:foo) klass.new - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) end it "fails if no instance is created" do expect do klass.any_instance.should_receive(:foo).and_return(1) - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) end @@ -432,7 +432,7 @@ class RSpec::SampleRspecTestClass;end expect do klass.any_instance.should_receive(:foo) klass.any_instance.should_receive(:bar) - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, 'Exactly one instance should have received the following message(s) but didn\'t: bar, foo') end @@ -470,7 +470,7 @@ class RSpec::SampleRspecTestClass;end klass.any_instance.should_receive(:foo) klass.should_receive(:woot) klass.new.foo - RSpec::Mocks.space.verify_all + verify_all end.to(raise_error(RSpec::Mocks::MockExpectationError) do |error| expect(error.message).not_to eq(existing_method_expectation_error_message) end) @@ -496,14 +496,14 @@ class RSpec::SampleRspecTestClass;end expect do klass.any_instance.should_receive(:existing_method) klass.new - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) end it "fails if no instance is created" do expect do klass.any_instance.should_receive(:existing_method) - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) end @@ -511,7 +511,7 @@ class RSpec::SampleRspecTestClass;end expect do klass.any_instance.should_receive(:existing_method) klass.any_instance.should_receive(:another_existing_method) - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, 'Exactly one instance should have received the following message(s) but didn\'t: another_existing_method, existing_method') end @@ -605,7 +605,7 @@ def foo; end it "fails when no instances are declared" do expect do klass.any_instance.should_receive(:foo).once - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) end @@ -613,7 +613,7 @@ def foo; end expect do klass.any_instance.should_receive(:foo).once klass.new - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) end @@ -719,7 +719,7 @@ def foo; end context "the 'never' constraint" do it "passes for 0 invocations" do klass.any_instance.should_receive(:foo).never - RSpec::Mocks.space.verify_all + verify_all end it "fails on the first invocation" do @@ -740,7 +740,7 @@ def foo; end expect do klass.any_instance.should_receive(:foo).never klass.any_instance.should_receive(:existing_method).and_return(5) - RSpec::Mocks.space.verify_all + verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) end end @@ -874,7 +874,7 @@ def foo; end klass.any_instance.stub(:existing_method).and_return(false) klass.any_instance.stub(:existing_method).and_return(true) - RSpec::Mocks.space.verify_all + verify_all expect(klass.new).to respond_to(:existing_method) expect(klass.new.existing_method).to eq(existing_method_return_value) end @@ -1035,7 +1035,7 @@ def dup(funky_option) instance = klass.new expect(instance.existing_method).to eq :stubbed_return_value - RSpec::Mocks.verify + verify_all expect(instance.existing_method).to eq :existing_method_return_value end diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index b28ad694f..821fcd6ba 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -64,7 +64,7 @@ module Mocks let(:expectation_error) do failure = nil begin - RSpec::Mocks.space.verify_all + verify_all rescue RSpec::Mocks::MockExpectationError => error failure = error end @@ -74,7 +74,7 @@ module Mocks it "sets up multiple expectations" do expect(obj).to receive_messages(:a => 1, :b => 2) obj.a - expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError + expect { verify_all }.to raise_error RSpec::Mocks::MockExpectationError end it 'fails with a sensible message' do diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 574d57ebc..4c7bb5a73 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -5,10 +5,6 @@ module Mocks describe Matchers::Receive do include_context "with syntax", :expect - def verify_all - ::RSpec::Mocks.space.verify_all - end - describe "expectations/allowances on any instance recorders" do include_context "with syntax", [:expect, :should] @@ -225,7 +221,7 @@ def receiver.method_missing(*a); end # a poor man's stub... it "removes the method double" do target.to receive(:foo).and_return(:baz) expect { - ::RSpec::Mocks.space.verify_all + verify_all }.to change { object.foo }.from(:baz).to(:bar) end end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index f54834609..d568e6932 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -8,6 +8,8 @@ expect do RSpec::Mocks::verify end.to raise_error(RSpec::Mocks::MockExpectationError) + + RSpec::Mocks.teardown # so the mocks aren't re-verified after this example end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d0e6a1b96..b08cbcdb5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -18,7 +18,10 @@ module VerifyAndResetHelpers def verify(object) - RSpec::Mocks.proxy_for(object).verify + proxy = RSpec::Mocks.proxy_for(object) + proxy.verify + ensure + proxy.reset # so it doesn't fail the verify after the example completes end def reset(object) @@ -27,6 +30,12 @@ def reset(object) def verify_all RSpec::Mocks.space.verify_all + ensure + reset_all + end + + def reset_all + RSpec::Mocks.space.reset_all end end From acd9a45705a74b2812ac533f344029ebb4a0786d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 09:14:16 -0800 Subject: [PATCH 0400/1179] Remove test we no longer need. The logic that required this test was removed in cd81094a. --- spec/rspec/mocks/double_spec.rb | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 09707f132..596d54020 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -560,34 +560,6 @@ def initialize(amount, units) verify double #should throw if reset didn't work end - it "works 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 Double#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 - - - @double.should_receive(:foobar) - @double.foobar - verify @double - - expect { @double.foobar }.to raise_error(RSpec::Mocks::MockExpectationError) - end - it "temporarily replaces a method stub on a double" do @double.stub(:msg).and_return(:stub_value) @double.should_receive(:msg).with(:arg).and_return(:double_value) From 6489742a438caefe97de8b4246fe7cd134bd6fc1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 09:50:05 -0800 Subject: [PATCH 0401/1179] Update spec to only specify the behavior we are keeping. We're going to change the behavior of test doubles that have been reset (so that they "expire" and can no longer be used). This spec was originally added in 847c66d55f2ec0ffc56aa27638a3469a03ddab3d where the point was to address an error while resetting a test double (which, in turn caused that method double to stick around into later examples and continue to fail). --- spec/rspec/mocks/double_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 596d54020..343a7e8c9 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -24,11 +24,10 @@ module Mocks expect(m.inspect).to match(/#/) end - it 'restores standard object methods on reset' do + it 'does not blow up when resetting standard object methods' do dbl = double(:tainted? => true) expect(dbl.tainted?).to eq(true) - reset dbl - expect(dbl.tainted?).to eq(false) + expect { reset dbl }.not_to raise_error end it 'does not get string vs symbol messages confused' do From 358fbdb8c25e111e80a7419d5925ca8845e141c2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 09:57:59 -0800 Subject: [PATCH 0402/1179] Split spec into two. The second part of this spec was added in 9357e4de76838157c8ea7d4a4ad6d0ae11882b22. We are changing test doubles so that they self-destruct after being reset. Currently, `verify` also resets, so verifying a test double twice will no longer be allowed. --- spec/rspec/mocks/double_spec.rb | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 343a7e8c9..2e6805462 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -138,14 +138,22 @@ module Mocks ) end - it "passes when receiving message specified as not to be received with wrong args" do - @double.should_not_receive(:not_expected).with("unexpected text") - @double.not_expected "really unexpected text" - verify @double + context "when specifying a message should not be received with specific args" do + context "using `should_not_receive`" do + it 'passes when receiving the message with different args' do + @double.should_not_receive(:not_expected).with("unexpected text") + @double.not_expected "really unexpected text" + verify @double + end + end - @double.should_receive(:not_expected).with("unexpected text").never - @double.not_expected "really unexpected text" - verify @double + context "using `should_receive().never`" do + it 'passes when receiving the message with different args' do + @double.should_receive(:not_expected).with("unexpected text").never + @double.not_expected "really unexpected text" + verify @double + end + end end it 'does not get confused when `should_not_received` is used with a string and symbol message' do From 0df48841c30d30251d8b6bf363f3953d337447d7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 16:35:09 -0800 Subject: [PATCH 0403/1179] Update teardown spec. We're going to change how pure test doubles behave after teardown, so update this to focus on partial doubles (which will not be changed). --- spec/rspec/mocks_spec.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index d568e6932..992c71fb7 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -14,13 +14,11 @@ end describe "::teardown" do - it "prevents further expectations from being set" do - foo = double - foo.should_receive(:bar) - RSpec::Mocks::teardown - expect do - foo.bar - end.to raise_error(RSpec::Mocks::OutsideOfExampleError) + it "resets method stubs" do + string = "foo" + allow(string).to receive(:bar) + RSpec::Mocks.teardown + expect { string.bar }.to raise_error(NoMethodError) end end From f83810b4668d5bc2ff2bc438400b572afc930a7e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 2 Jan 2014 23:25:54 -0800 Subject: [PATCH 0404/1179] Make test doubles self-destruct after the example completes. Test doubles are not designed to be used outside of the example they were created in. --- lib/rspec/mocks/error_generator.rb | 8 +++++ lib/rspec/mocks/errors.rb | 4 +++ lib/rspec/mocks/proxy.rb | 15 ++++++++- lib/rspec/mocks/proxy_for_nil.rb | 2 +- lib/rspec/mocks/test_double.rb | 12 ++++++- spec/rspec/mocks/double_spec.rb | 54 ++++++++++++++++++++++++++++++ 6 files changed, 92 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 5c3c24321..ca383a18c 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -63,6 +63,14 @@ def raise_arity_error(calculator, actual) ] end + def raise_expired_test_double_error + raise ExpiredTestDoubleError, + "#{intro} was originally created in one example but has leaked into " + + "another example and can no longer be used. rspec-mocks' doubles are " + + "designed to only last for one example, and you need to create a new " + + "one in each example you wish to use it for." + end + # @private def received_part_of_expectation_error(actual_received_count, *args) "received: #{count_message(actual_received_count)}" + diff --git a/lib/rspec/mocks/errors.rb b/lib/rspec/mocks/errors.rb index 0e8787e23..63832101b 100644 --- a/lib/rspec/mocks/errors.rb +++ b/lib/rspec/mocks/errors.rb @@ -4,6 +4,10 @@ module Mocks class MockExpectationError < Exception end + # @private + class ExpiredTestDoubleError < MockExpectationError + end + # @private class AmbiguousReturnError < StandardError end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index a9d6899a9..706c88f28 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -128,7 +128,6 @@ def verify # @private def reset - @method_doubles.each_value {|d| d.reset} @messages_received.clear end @@ -235,6 +234,15 @@ def find_almost_matching_stub(method_name, *args) end end + # @private + class TestDoubleProxy < Proxy + def reset + @method_doubles.clear + object.__disallow_further_usage! + super + end + end + # @private class PartialDoubleProxy < Proxy def method_handle_for(message) @@ -268,6 +276,11 @@ def visibility_for(method_name) MethodReference.method_visibility_for(@object, method_name) || :public end + def reset + @method_doubles.each_value {|d| d.reset} + super + end + private def any_instance_class_recorder_observing_method?(klass, method_name) diff --git a/lib/rspec/mocks/proxy_for_nil.rb b/lib/rspec/mocks/proxy_for_nil.rb index b58cb3de7..ef00d62c6 100644 --- a/lib/rspec/mocks/proxy_for_nil.rb +++ b/lib/rspec/mocks/proxy_for_nil.rb @@ -1,7 +1,7 @@ module RSpec module Mocks # @private - class ProxyForNil < Proxy + class ProxyForNil < PartialDoubleProxy def initialize(order_group) @warn_about_expectations = true diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 5275bba66..9ec8bf03a 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -62,12 +62,17 @@ def respond_to?(message, incl_private=false) # @private def __build_mock_proxy(order_group) - Proxy.new(self, order_group, @name) + __raise_expired_error or TestDoubleProxy.new(self, order_group, @name) + end + + def __disallow_further_usage! + @__expired = true end private def __initialize_as_test_double(name=nil, stubs={}) + @__expired = false if Hash === name && stubs.empty? stubs = name @name = nil @@ -109,6 +114,11 @@ def assign_stubs(stubs) def __mock_proxy ::RSpec::Mocks.proxy_for(self) end + + def __raise_expired_error + return false unless @__expired + ErrorGenerator.new(self, @name).raise_expired_test_double_error + end end # A generic test double object. `double`, `instance_double` and friends diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 2e6805462..94d6d190b 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -50,6 +50,60 @@ module Mocks expect(dbl.foo = "bar").to eq("bar") end + context "after it has been torn down" do + let(:dbl) { double } + + before do + expect(dbl).to receive(:foo).at_least(:once) + allow(dbl).to receive(:bar) + dbl.foo + + RSpec::Mocks.verify + RSpec::Mocks.teardown + RSpec::Mocks.setup + end + + it 'disallows previously mocked methods' do + expect { dbl.foo }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows previously stubbed methods' do + expect { dbl.bar }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows stubbing new methods (with receive)' do + expect { + allow(dbl).to receive(:bazz) + }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows stubbing new methods (with receive_messages)' do + expect { + allow(dbl).to receive_messages(:bazz => 3) + }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows stubbing new message chains' do + expect { + allow(dbl).to receive_message_chain(:bazz, :bam, :goo) + }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows mocking new methods' do + expect { + expect(dbl).to receive(:bazz) + }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows being turned into a null object' do + expect { dbl.as_null_object }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows being checked for nullness' do + expect { dbl.null_object? }.to raise_error(ExpiredTestDoubleError) + end + end + it "reports line number of expectation of unreceived message" do expected_error_line = __LINE__; @double.should_receive(:wont_happen).with("x", 3) begin From 3138bbab1686e3e56b42dea0b754273ac017c63a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 18:10:15 -0800 Subject: [PATCH 0405/1179] No need for ProxyForNil to have its own file. --- lib/rspec/mocks/framework.rb | 1 - lib/rspec/mocks/proxy.rb | 33 ++++++++++++++++++++++++++++ lib/rspec/mocks/proxy_for_nil.rb | 37 -------------------------------- 3 files changed, 33 insertions(+), 38 deletions(-) delete mode 100644 lib/rspec/mocks/proxy_for_nil.rb diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 050d0650b..6c6daa92d 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -9,7 +9,6 @@ require 'rspec/mocks/argument_matchers' require 'rspec/mocks/example_methods' require 'rspec/mocks/proxy' -require 'rspec/mocks/proxy_for_nil' require 'rspec/mocks/test_double' require 'rspec/mocks/argument_list_matcher' require 'rspec/mocks/message_expectation' diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 706c88f28..a7564dd2c 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -290,5 +290,38 @@ def any_instance_class_recorder_observing_method?(klass, method_name) any_instance_class_recorder_observing_method?(superklass, method_name) end end + + # @private + class ProxyForNil < PartialDoubleProxy + def initialize(order_group) + @warn_about_expectations = true + super(nil, order_group) + end + + attr_accessor :warn_about_expectations + alias warn_about_expectations? warn_about_expectations + + def add_message_expectation(location, method_name, opts={}, &block) + warn(method_name) if warn_about_expectations? + super + end + + def add_negative_message_expectation(location, method_name, &implementation) + warn(method_name) if warn_about_expectations? + super + end + + def add_stub(location, method_name, opts={}, &implementation) + warn(method_name) if warn_about_expectations? + super + end + + private + + def warn method_name + source = CallerFilter.first_non_rspec_line + Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.") + end + end end end diff --git a/lib/rspec/mocks/proxy_for_nil.rb b/lib/rspec/mocks/proxy_for_nil.rb deleted file mode 100644 index ef00d62c6..000000000 --- a/lib/rspec/mocks/proxy_for_nil.rb +++ /dev/null @@ -1,37 +0,0 @@ -module RSpec - module Mocks - # @private - class ProxyForNil < PartialDoubleProxy - - def initialize(order_group) - @warn_about_expectations = true - super nil, order_group - end - attr_accessor :warn_about_expectations - alias warn_about_expectations? warn_about_expectations - - def add_message_expectation(location, method_name, opts={}, &block) - warn(method_name) if warn_about_expectations? - super - end - - def add_negative_message_expectation(location, method_name, &implementation) - warn(method_name) if warn_about_expectations? - super - end - - def add_stub(location, method_name, opts={}, &implementation) - warn(method_name) if warn_about_expectations? - super - end - - private - - def warn method_name - source = CallerFilter.first_non_rspec_line - Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.") - end - - end - end -end From b639f7e52c5c7f19c79e57f85fa78f4d568e583b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 18:12:07 -0800 Subject: [PATCH 0406/1179] Centralize error classes in error_generator.rb. Also, document some of them as public since users may want to rescue them, or specify a block will raise one of these errors (e.g. in an rspec-mocks extension gem). --- lib/rspec/mocks/error_generator.rb | 18 ++++++++++++++++++ lib/rspec/mocks/error_space.rb | 4 ---- lib/rspec/mocks/errors.rb | 16 ---------------- lib/rspec/mocks/framework.rb | 1 - lib/rspec/mocks/targets.rb | 3 --- 5 files changed, 18 insertions(+), 24 deletions(-) delete mode 100644 lib/rspec/mocks/errors.rb diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index ca383a18c..7d86e6036 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -1,5 +1,23 @@ module RSpec module Mocks + # @public + # Raised when a message expectation is not satisfied. + MockExpectationError = Class.new(Exception) + + # @public + # Raised when a test double is used after it has been torn + # down (typically at the end of an rspec-core example). + ExpiredTestDoubleError = Class.new(MockExpectationError) + + # @public + # Raised when doubles or partial doubles are used outside of the per-test lifecycle. + OutsideOfExampleError = Class.new(StandardError) + + # @private + UnsupportedMatcherError = Class.new(StandardError) + # @private + NegationUnsupportedError = Class.new(StandardError) + # @private class ErrorGenerator attr_writer :opts diff --git a/lib/rspec/mocks/error_space.rb b/lib/rspec/mocks/error_space.rb index 599065749..ecddad1cf 100644 --- a/lib/rspec/mocks/error_space.rb +++ b/lib/rspec/mocks/error_space.rb @@ -1,9 +1,5 @@ module RSpec module Mocks - # @private - # Raised when doubles or partial doubles are used outside of the per-test lifecycle. - OutsideOfExampleError = Class.new(StandardError) - # @private class ErrorSpace def proxy_for(*args) diff --git a/lib/rspec/mocks/errors.rb b/lib/rspec/mocks/errors.rb deleted file mode 100644 index 63832101b..000000000 --- a/lib/rspec/mocks/errors.rb +++ /dev/null @@ -1,16 +0,0 @@ -module RSpec - module Mocks - # @private - class MockExpectationError < Exception - end - - # @private - class ExpiredTestDoubleError < MockExpectationError - end - - # @private - class AmbiguousReturnError < StandardError - end - end -end - diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb index 6c6daa92d..223e88c33 100644 --- a/lib/rspec/mocks/framework.rb +++ b/lib/rspec/mocks/framework.rb @@ -13,7 +13,6 @@ require 'rspec/mocks/argument_list_matcher' require 'rspec/mocks/message_expectation' require 'rspec/mocks/order_group' -require 'rspec/mocks/errors' require 'rspec/mocks/error_generator' require 'rspec/mocks/space' require 'rspec/mocks/extensions/marshal' diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 00c75309c..a466c2e29 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -1,8 +1,5 @@ module RSpec module Mocks - UnsupportedMatcherError = Class.new(StandardError) - NegationUnsupportedError = Class.new(StandardError) - class TargetBase def initialize(target) @target = target From 83e56b00a08ef567502c2fd7a8f32ae3f62fd61b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 20:42:28 -0800 Subject: [PATCH 0407/1179] Move error_space.rb into space.rb There's no need for a separate file and there's going to be a bit of interaction between multiple spaces at various layers in a stack so having it in one file will make it easier to see how it relates. --- lib/rspec/mocks.rb | 7 +++---- lib/rspec/mocks/error_space.rb | 31 ------------------------------- lib/rspec/mocks/space.rb | 31 +++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 35 deletions(-) delete mode 100644 lib/rspec/mocks/error_space.rb diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index f0eefb9bd..f850122be 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -1,7 +1,6 @@ require 'rspec/mocks/framework' require 'rspec/mocks/version' require 'rspec/support' -require "rspec/mocks/error_space" module RSpec # Contains top-level utility methods. While this contains a few @@ -9,7 +8,7 @@ module RSpec # a test or example. They exist primarily for integration with # test frameworks (such as rspec-core). module Mocks - ERROR_SPACE = RSpec::Mocks::ErrorSpace.new + ROOT_SPACE = RSpec::Mocks::RootSpace.new MOCK_SPACE = RSpec::Mocks::Space.new class << self @@ -18,7 +17,7 @@ class << self attr_accessor :space end - self.space = ERROR_SPACE + self.space = ROOT_SPACE # Performs per-test/example setup. This should be called before # an test or example begins. @@ -37,7 +36,7 @@ def self.verify # each example, even if an error was raised during the example. def self.teardown space.reset_all - self.space = ERROR_SPACE + self.space = ROOT_SPACE end # Adds an allowance (stub) on `subject` diff --git a/lib/rspec/mocks/error_space.rb b/lib/rspec/mocks/error_space.rb deleted file mode 100644 index ecddad1cf..000000000 --- a/lib/rspec/mocks/error_space.rb +++ /dev/null @@ -1,31 +0,0 @@ -module RSpec - module Mocks - # @private - class ErrorSpace - def proxy_for(*args) - raise_lifecycle_message - end - - def any_instance_recorder_for(*args) - raise_lifecycle_message - end - - def reset_all - end - - def verify_all - end - - def registered?(object) - false - end - - private - - def raise_lifecycle_message - raise OutsideOfExampleError, "The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported." - end - end - - end -end diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 3e60bd774..bfd54774b 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -1,5 +1,36 @@ module RSpec module Mocks + # @private + # Provides a default space implementation for outside + # the scope of an example. Called "root" because it serves + # as the root of the space stack. + class RootSpace + def proxy_for(*args) + raise_lifecycle_message + end + + def any_instance_recorder_for(*args) + raise_lifecycle_message + end + + def reset_all + end + + def verify_all + end + + def registered?(object) + false + end + + private + + def raise_lifecycle_message + raise OutsideOfExampleError, + "The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported." + end + end + # @api private class Space attr_reader :proxies, :any_instance_recorders From e6858679feb9755e0ef6a3eb9c6c6c5e2ef8e087 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 20:50:30 -0800 Subject: [PATCH 0408/1179] Remove an unnecessary layer of indirection. * This should be faster (but almost certainly not noticably so). * It was odd to expose these methods off of `RSpec::Mocks` given that they are not intended for usage outside rspec-mocks. * We weren't getting any benefit from these except for not having to type `.space`. --- lib/rspec/mocks.rb | 22 ++----------------- .../mocks/any_instance/expectation_chain.rb | 2 +- lib/rspec/mocks/any_instance/recorder.rb | 12 +++++----- lib/rspec/mocks/any_instance/stub_chain.rb | 2 +- lib/rspec/mocks/matchers/have_received.rb | 2 +- lib/rspec/mocks/matchers/receive.rb | 4 ++-- .../mocks/matchers/receive_message_chain.rb | 4 ++-- lib/rspec/mocks/matchers/receive_messages.rb | 4 ++-- lib/rspec/mocks/message_chain.rb | 4 ++-- lib/rspec/mocks/proxy.rb | 4 ++-- lib/rspec/mocks/syntax.rb | 6 ++--- lib/rspec/mocks/test_double.rb | 2 +- spec/rspec/mocks/any_instance_spec.rb | 2 +- spec/spec_helper.rb | 4 ++-- 14 files changed, 29 insertions(+), 45 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index f850122be..1dea19912 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -55,7 +55,7 @@ def self.allow_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } - ::RSpec::Mocks.proxy_for(subject). + ::RSpec::Mocks.space.proxy_for(subject). add_stub(orig_caller, message, opts, &block) end @@ -74,28 +74,10 @@ def self.expect_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } - ::RSpec::Mocks.proxy_for(subject). + ::RSpec::Mocks.space.proxy_for(subject). add_message_expectation(orig_caller, message, opts, &block) end - # @api private - # Returns the mock proxy for the given object. - def self.proxy_for(object) - space.proxy_for(object) - end - - # @api private - # Returns the mock proxies for instances of the given class. - def self.proxies_of(klass) - space.proxies_of(klass) - end - - # @api private - # Returns the any instance recorder for the given class. - def self.any_instance_recorder_for(klass) - space.any_instance_recorder_for(klass) - end - # @private IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored' end diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 6646f6675..073cf096f 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -23,7 +23,7 @@ class PositiveExpectationChain < ExpectationChain private def create_message_expectation_on(instance) - proxy = ::RSpec::Mocks.proxy_for(instance) + proxy = ::RSpec::Mocks.space.proxy_for(instance) expected_from = IGNORED_BACKTRACE_LINE me = proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block) if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index e3f63977e..fa4700fe7 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -82,7 +82,7 @@ def unstub(method_name) raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" end message_chains.remove_stub_chains_for!(method_name) - ::RSpec::Mocks.proxies_of(@klass).each do |proxy| + ::RSpec::Mocks.space.proxies_of(@klass).each do |proxy| stubs[method_name].each { |stub| proxy.remove_single_stub(method_name, stub) } end stubs[method_name].clear @@ -134,7 +134,8 @@ def stop_observing!(method_name) restore_method!(method_name) @observed_methods.delete(method_name) super_class_observers_for(method_name).each do |ancestor| - ::RSpec::Mocks.any_instance_recorder_for(ancestor).stop_observing!(method_name) + ::RSpec::Mocks.space. + any_instance_recorder_for(ancestor).stop_observing!(method_name) end end @@ -143,7 +144,8 @@ def stop_observing!(method_name) def ancestor_is_an_observer?(method_name) lambda do |ancestor| unless ancestor == @klass - ::RSpec::Mocks.any_instance_recorder_for(ancestor).already_observing?(method_name) + ::RSpec::Mocks.space. + any_instance_recorder_for(ancestor).already_observing?(method_name) end end end @@ -213,7 +215,7 @@ def observe!(method_name) backup_method!(method_name) @klass.__send__(:define_method, method_name) do |*args, &blk| klass = ::RSpec::Support.method_handle_for(self, method_name).owner - ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name) + ::RSpec::Mocks.space.any_instance_recorder_for(klass).playback!(self, method_name) self.__send__(method_name, *args, &blk) end end @@ -222,7 +224,7 @@ def mark_invoked!(method_name) backup_method!(method_name) @klass.__send__(:define_method, method_name) do |*args, &blk| klass = ::RSpec::Support.method_handle_for(self, method_name).owner - invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name) + invoked_instance = ::RSpec::Mocks.space.any_instance_recorder_for(klass).instance_that_received(method_name) raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{self.inspect} but has already been received by #{invoked_instance}" end end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index ef84a4937..e8b45adfb 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -12,7 +12,7 @@ def expectation_fulfilled? private def create_message_expectation_on(instance) - proxy = ::RSpec::Mocks.proxy_for(instance) + proxy = ::RSpec::Mocks.space.proxy_for(instance) expected_from = IGNORED_BACKTRACE_LINE stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block) @recorder.stubs[stub.message] << stub diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index fe801d55f..c0c5ae755 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -93,7 +93,7 @@ def expected_messages_received_in_order? end def mock_proxy - RSpec::Mocks.proxy_for(@subject) + RSpec::Mocks.space.proxy_for(@subject) end end end diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 52e29345c..3a4245afc 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -71,12 +71,12 @@ def warn_if_any_instance(expression, subject) end def setup_mock_proxy_method_substitute(subject, method, block) - proxy = ::RSpec::Mocks.proxy_for(subject) + proxy = ::RSpec::Mocks.space.proxy_for(subject) setup_method_substitute(proxy, method, block, @backtrace_line) end def setup_any_instance_method_substitute(subject, method, block) - any_instance_recorder = ::RSpec::Mocks.any_instance_recorder_for(subject) + any_instance_recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject) setup_method_substitute(any_instance_recorder, method, block) end diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index a8be3fa2d..07108816d 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -26,13 +26,13 @@ def setup_allowance(subject, &block) end def setup_any_instance_allowance(subject, &block) - recorder = ::RSpec::Mocks.any_instance_recorder_for(subject) + recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject) chain = recorder.stub_chain(*@chain, &(@block || block)) replay_customizations(chain) end def setup_any_instance_expectation(subject, &block) - recorder = ::RSpec::Mocks.any_instance_recorder_for(subject) + recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject) chain = recorder.expect_chain(*@chain, &(@block || block)) replay_customizations(chain) end diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index 67795730f..719e9b3e3 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -54,11 +54,11 @@ def warn_about_block private def proxy_on(subject) - ::RSpec::Mocks.proxy_for(subject) + ::RSpec::Mocks.space.proxy_for(subject) end def any_instance_of(subject) - ::RSpec::Mocks.any_instance_recorder_for(subject) + ::RSpec::Mocks.space.any_instance_recorder_for(subject) end def each_message_on(host) diff --git a/lib/rspec/mocks/message_chain.rb b/lib/rspec/mocks/message_chain.rb index a46f87de6..6dbdd1cf4 100644 --- a/lib/rspec/mocks/message_chain.rb +++ b/lib/rspec/mocks/message_chain.rb @@ -51,12 +51,12 @@ def format_chain(*chain, &blk) end def find_matching_stub - ::RSpec::Mocks.proxy_for(object). + ::RSpec::Mocks.space.proxy_for(object). __send__(:find_matching_method_stub, chain.first.to_sym) end def find_matching_expectation - ::RSpec::Mocks.proxy_for(object). + ::RSpec::Mocks.space.proxy_for(object). __send__(:find_matching_expectation, chain.first.to_sym) end end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index a7564dd2c..f3fb3d285 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -247,7 +247,7 @@ def reset class PartialDoubleProxy < Proxy def method_handle_for(message) if any_instance_class_recorder_observing_method?(@object.class, message) - message = ::RSpec::Mocks. + message = ::RSpec::Mocks.space. any_instance_recorder_for(@object.class). build_alias_method_name(message) end @@ -284,7 +284,7 @@ def reset private def any_instance_class_recorder_observing_method?(klass, method_name) - return true if ::RSpec::Mocks.any_instance_recorder_for(klass).already_observing?(method_name) + return true if ::RSpec::Mocks.space.any_instance_recorder_for(klass).already_observing?(method_name) superklass = klass.superclass return false if superklass.nil? any_instance_class_recorder_observing_method?(superklass, method_name) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 5f389de15..7a20872b8 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -63,7 +63,7 @@ def stub_chain(*chain, &blk) def as_null_object ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) @_null_object = true - ::RSpec::Mocks.proxy_for(self).as_null_object + ::RSpec::Mocks.space.proxy_for(self).as_null_object end def null_object? @@ -73,14 +73,14 @@ def null_object? def received_message?(message, *args, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) - ::RSpec::Mocks.proxy_for(self).received_message?(message, *args, &block) + ::RSpec::Mocks.space.proxy_for(self).received_message?(message, *args, &block) end unless Class.respond_to? :any_instance Class.class_exec do def any_instance ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) - ::RSpec::Mocks.any_instance_recorder_for(self) + ::RSpec::Mocks.space.any_instance_recorder_for(self) end end end diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 9ec8bf03a..cc1200c9f 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -112,7 +112,7 @@ def assign_stubs(stubs) end def __mock_proxy - ::RSpec::Mocks.proxy_for(self) + ::RSpec::Mocks.space.proxy_for(self) end def __raise_expired_error diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index e687397a3..19a4017e5 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -249,7 +249,7 @@ def private_method; :private_method_return_value; end # Simply resetting the proxy doesn't work # what we need to have happen is - # ::RSpec::Mocks.any_instance_recorder_for(klass).stop_all_observation! + # ::RSpec::Mocks.space.any_instance_recorder_for(klass).stop_all_observation! # but that is never invoked in :: expect { verify_all diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b08cbcdb5..5c61b2665 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -18,14 +18,14 @@ module VerifyAndResetHelpers def verify(object) - proxy = RSpec::Mocks.proxy_for(object) + proxy = RSpec::Mocks.space.proxy_for(object) proxy.verify ensure proxy.reset # so it doesn't fail the verify after the example completes end def reset(object) - RSpec::Mocks.proxy_for(object).reset + RSpec::Mocks.space.proxy_for(object).reset end def verify_all From 513bdb1e6fd9aaedb6cbf407a857d9a90ff74e25 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 4 Jan 2014 21:42:46 -0800 Subject: [PATCH 0409/1179] Having a private `pretty_print` method causes problems. When rspec-expectations tries to diff the object in a failure message, it blows up when the `pp` library tries to print it. --- lib/rspec/mocks/error_generator.rb | 8 ++++---- spec/rspec/mocks/space_spec.rb | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 7d86e6036..66c87be8f 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -215,12 +215,12 @@ def received_arg_list(*args) end def count_message(count, expectation_count_type=nil) - return "at least #{pretty_print(count.abs)}" if count < 0 || expectation_count_type == :at_least - return "at most #{pretty_print(count)}" if expectation_count_type == :at_most - return pretty_print(count) + return "at least #{times(count.abs)}" if count < 0 || expectation_count_type == :at_least + return "at most #{times(count)}" if expectation_count_type == :at_most + return times(count) end - def pretty_print(count) + def times(count) "#{count} time#{count == 1 ? '' : 's'}" end diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index 9a59e7f0f..cf73e2292 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -29,5 +29,17 @@ module RSpec::Mocks end end + it 'can be diffed in a failure when it has references to an error generator via a proxy' do + space1 = Space.new + space2 = Space.new + + space1.proxy_for("") + space2.proxy_for("") + + expect { + expect(space1).to eq(space2) + }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /Diff/) + end + end end From 9da93fd983982c4365f413ad6bac61886007baaa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 23:12:58 -0800 Subject: [PATCH 0410/1179] Refactor: store const mutators in mock space. This unifies global storage of modified things in one place. --- lib/rspec/mocks/mutate_const.rb | 40 +++++---------------------------- lib/rspec/mocks/space.rb | 12 +++++++++- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index d79ab15f7..bab9992af 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -146,7 +146,7 @@ def self.unmutated(name) # @return [Constant] an object contaning information about the named # constant. def self.original(name) - mutator = ConstantMutator.find(name) + mutator = ::RSpec::Mocks.space.constant_mutator_for(name) mutator ? mutator.to_constant : unmutated(name) end end @@ -239,7 +239,7 @@ def to_constant const end - def rspec_reset + def reset @context.const_set(@const_name, @original_value) end end @@ -268,7 +268,7 @@ def to_constant const end - def rspec_reset + def reset @context.__send__(:remove_const, @const_name) @context.const_set(@const_name, @original_value) end @@ -338,7 +338,7 @@ def to_constant const end - def rspec_reset + def reset @deepest_defined_const.__send__(:remove_const, @const_to_remove) end end @@ -349,40 +349,10 @@ def rspec_reset # # @api private def self.mutate(mutator) - register_mutator(mutator) + ::RSpec::Mocks.space.register_constant_mutator(mutator) mutator.mutate end - # Resets all stubbed constants. This is called automatically - # by rspec-mocks when an example finishes. - # - # @api private - def self.reset_all - # We use reverse order so that if the same constant - # was stubbed multiple times, the original value gets - # properly restored. - mutators.reverse.each { |s| s.rspec_reset } - - mutators.clear - end - - # The list of constant mutators that have been used for - # the current example. - # - # @api private - def self.mutators - @mutators ||= [] - end - - # @api private - def self.register_mutator(mutator) - mutators << mutator - end - - def self.find(name) - mutators.find { |s| s.full_constant_name == name } - end - # Used internally by the constant stubbing to raise a helpful # error when a constant like "A::B::C" is stubbed and A::B is # not a module (and thus, it's impossible to define "A::B::C" diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index bfd54774b..d1f0ad755 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -38,6 +38,7 @@ class Space def initialize @proxies = {} @any_instance_recorders = {} + @constant_mutators = [] end def verify_all @@ -51,7 +52,7 @@ def verify_all end def reset_all - ConstantMutator.reset_all + @constant_mutators.reverse.each { |mut| mut.reset } proxies.each_value do |object| object.reset @@ -60,12 +61,21 @@ def reset_all proxies.clear any_instance_recorders.clear expectation_ordering.clear + @constant_mutators.clear end def expectation_ordering @expectation_ordering ||= OrderGroup.new end + def register_constant_mutator(mutator) + @constant_mutators << mutator + end + + def constant_mutator_for(name) + @constant_mutators.find { |m| m.full_constant_name == name } + end + def any_instance_recorder_for(klass) id = klass.__id__ any_instance_recorders.fetch(id) do From 7c94a6fbf357820dc5a7bee9f8da4392b05ba71e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Jan 2014 23:34:47 -0800 Subject: [PATCH 0411/1179] Re-work spec file a bit. - Make it more general (it's not just about stubbing). - Remove unused lines. - Improve doc output (`include_examples` does not create a nested group). --- ..._before_all_spec.rb => before_all_spec.rb} | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) rename spec/rspec/mocks/{stubbing_in_before_all_spec.rb => before_all_spec.rb} (69%) diff --git a/spec/rspec/mocks/stubbing_in_before_all_spec.rb b/spec/rspec/mocks/before_all_spec.rb similarity index 69% rename from spec/rspec/mocks/stubbing_in_before_all_spec.rb rename to spec/rspec/mocks/before_all_spec.rb index 5086da133..f0024ae9e 100644 --- a/spec/rspec/mocks/stubbing_in_before_all_spec.rb +++ b/spec/rspec/mocks/before_all_spec.rb @@ -1,10 +1,7 @@ require "spec_helper" -require "delegate" -describe "Stubbing/mocking methods in before(:all) blocks" do - old_rspec = nil - - shared_examples_for "A stub/mock in a before(:all) block" do +describe "Using rspec-mocks features in before(:all) blocks" do + shared_examples_for "fails in a before(:all) block" do the_error = nil before(:all) do begin @@ -22,7 +19,7 @@ end describe "#stub" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.stub(:foo) end @@ -30,7 +27,7 @@ def use_rspec_mocks end describe "#unstub" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.unstub(:foo) end @@ -38,7 +35,7 @@ def use_rspec_mocks end describe "#should_receive" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.should_receive(:foo) end @@ -46,7 +43,7 @@ def use_rspec_mocks end describe "#should_not_receive" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.should_not_receive(:foo) end @@ -54,7 +51,7 @@ def use_rspec_mocks end describe "#any_instance" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.any_instance.should_receive(:foo) end @@ -62,7 +59,7 @@ def use_rspec_mocks end describe "#stub_chain" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.stub_chain(:foo) end @@ -70,7 +67,7 @@ def use_rspec_mocks end describe "#expect(...).to receive" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks expect(Object).to receive(:foo) end @@ -78,7 +75,7 @@ def use_rspec_mocks end describe "#allow(...).to receive" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks allow(Object).to receive(:foo) end @@ -86,7 +83,7 @@ def use_rspec_mocks end describe "#expect_any_instance_of(...).to receive" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks expect_any_instance_of(Object).to receive(:foo) end @@ -94,7 +91,7 @@ def use_rspec_mocks end describe "#allow_any_instance_of(...).to receive" do - it_behaves_like "A stub/mock in a before(:all) block" do + include_examples "fails in a before(:all) block" do def use_rspec_mocks allow_any_instance_of(Object).to receive(:foo) end From d89e6804c37b663b62f9135fb1f934fc2212a1eb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 4 Jan 2014 00:00:49 -0800 Subject: [PATCH 0412/1179] Don't allow const stubbing/hiding from before(:all). --- lib/rspec/mocks/mutate_const.rb | 7 +++--- lib/rspec/mocks/space.rb | 4 ++++ spec/rspec/mocks/before_all_spec.rb | 32 +++++++++++++++++++++++++++ spec/rspec/mocks/mutate_const_spec.rb | 12 ++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index bab9992af..73db1ecc4 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -138,7 +138,6 @@ def self.unmutated(name) const end - private_class_method :unmutated # Queries rspec-mocks to find out information about the named constant. # @@ -190,8 +189,6 @@ def self.stub(constant_name, value, options = {}) # so you can hide constants in other contexts (e.g. helper # classes). def self.hide(constant_name) - return unless recursive_const_defined?(constant_name) - mutate(ConstantHider.new(constant_name, nil, { })) nil end @@ -225,6 +222,7 @@ def to_constant # @api private class ConstantHider < BaseMutator def mutate + return unless @defined = recursive_const_defined?(full_constant_name) @context = recursive_const_get(@context_parts.join('::')) @original_value = get_const_defined_on(@context, @const_name) @@ -232,6 +230,8 @@ def mutate end def to_constant + return Constant.unmutated(full_constant_name) unless @defined + const = super const.hidden = true const.previously_defined = true @@ -240,6 +240,7 @@ def to_constant end def reset + return unless @defined @context.const_set(@const_name, @original_value) end end diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index d1f0ad755..b46d7a2dc 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -13,6 +13,10 @@ def any_instance_recorder_for(*args) raise_lifecycle_message end + def register_constant_mutator(mutator) + raise_lifecycle_message + end + def reset_all end diff --git a/spec/rspec/mocks/before_all_spec.rb b/spec/rspec/mocks/before_all_spec.rb index f0024ae9e..fe71d8d3f 100644 --- a/spec/rspec/mocks/before_all_spec.rb +++ b/spec/rspec/mocks/before_all_spec.rb @@ -18,6 +18,38 @@ end end + describe "#stub_const" do + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + stub_const("SomeNewConst", Class.new) + end + + it 'does not stub the const' do + expect(defined?(SomeNewConst)).to be_falsey + end + end + end + + describe "#hide_const(for an undefined const)" do + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + hide_const("Foo") + end + end + end + + describe "#hide_const(for a defined const)" do + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + hide_const("Float") + end + + it 'does not hide the const' do + expect(defined?(Float)).to be_truthy + end + end + end + describe "#stub" do include_examples "fails in a before(:all) block" do def use_rspec_mocks diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 16fbd07f5..197650f76 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -488,6 +488,18 @@ def change_const_value_to(value) it("returns nil for the original value") { expect(const.original_value).to be_nil } end + context 'for a previously undefined hidden constant' do + before { hide_const("SomeUndefinedConst") } + let(:const) { Constant.original("SomeUndefinedConst") } + + it("exposes its name") { expect(const.name).to eq("SomeUndefinedConst") } + it("indicates it was previously undefined") { expect(const).not_to be_previously_defined } + it("indicates it has not been mutated") { expect(const).not_to be_mutated } + it("indicates it has not not been stubbed") { expect(const).not_to be_stubbed } + it("indicates it has not been hidden") { expect(const).not_to be_hidden } + it("returns nil for the original value") { expect(const.original_value).to be_nil } + end + context 'for a previously defined hidden constant' do before { hide_const("TestClass::M") } let(:const) { Constant.original("TestClass::M") } From c28b37992fceff6b2997194ac890e4e21f58db63 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 4 Jan 2014 00:29:22 -0800 Subject: [PATCH 0413/1179] Refactor space tracking so it uses a stack and allows nesting. --- lib/rspec/mocks.rb | 26 ++-- lib/rspec/mocks/extensions/marshal.rb | 2 +- lib/rspec/mocks/space.rb | 71 ++++++++-- spec/rspec/mocks/extensions/marshal_spec.rb | 17 --- spec/rspec/mocks/space_spec.rb | 143 +++++++++++++++++++- spec/rspec/mocks_spec.rb | 32 ++++- 6 files changed, 234 insertions(+), 57 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 1dea19912..69fd1d66b 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -8,21 +8,10 @@ module RSpec # a test or example. They exist primarily for integration with # test frameworks (such as rspec-core). module Mocks - ROOT_SPACE = RSpec::Mocks::RootSpace.new - MOCK_SPACE = RSpec::Mocks::Space.new - - class << self - # Stores rspec-mocks' global state. - # @api private - attr_accessor :space - end - - self.space = ROOT_SPACE - # Performs per-test/example setup. This should be called before # an test or example begins. def self.setup - self.space = MOCK_SPACE + @space_stack << (@space = space.new_scope) end # Verifies any message expectations that were set during the @@ -36,7 +25,8 @@ def self.verify # each example, even if an error was raised during the example. def self.teardown space.reset_all - self.space = ROOT_SPACE + @space_stack.pop + @space = @space_stack.last || @root_space end # Adds an allowance (stub) on `subject` @@ -55,8 +45,7 @@ def self.allow_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } - ::RSpec::Mocks.space.proxy_for(subject). - add_stub(orig_caller, message, opts, &block) + space.proxy_for(subject).add_stub(orig_caller, message, opts, &block) end # Sets a message expectation on `subject`. @@ -74,10 +63,13 @@ def self.expect_message(subject, message, opts={}, &block) orig_caller = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } - ::RSpec::Mocks.space.proxy_for(subject). - add_message_expectation(orig_caller, message, opts, &block) + space.proxy_for(subject).add_message_expectation(orig_caller, message, opts, &block) end + class << self; attr_reader :space; end + @space_stack = [] + @root_space = @space = RSpec::Mocks::RootSpace.new + # @private IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored' end diff --git a/lib/rspec/mocks/extensions/marshal.rb b/lib/rspec/mocks/extensions/marshal.rb index b8cf7b5df..47b8fbfea 100644 --- a/lib/rspec/mocks/extensions/marshal.rb +++ b/lib/rspec/mocks/extensions/marshal.rb @@ -3,7 +3,7 @@ class << self # Duplicates any mock objects before serialization. Otherwise, # serialization will fail because methods exist on the singleton class. def dump_with_mocks(object, *rest) - if ::RSpec::Mocks.space.nil? || !::RSpec::Mocks.space.registered?(object) || NilClass === object + if !::RSpec::Mocks.space.registered?(object) || NilClass === object dump_without_mocks(object, *rest) else dump_without_mocks(object.dup, *rest) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index b46d7a2dc..e40d8ef78 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -27,6 +27,10 @@ def registered?(object) false end + def new_scope + Space.new + end + private def raise_lifecycle_message @@ -45,6 +49,10 @@ def initialize @constant_mutators = [] end + def new_scope + NestedSpace.new(self) + end + def verify_all proxies.each_value do |object| object.verify @@ -83,7 +91,7 @@ def constant_mutator_for(name) def any_instance_recorder_for(klass) id = klass.__id__ any_instance_recorders.fetch(id) do - any_instance_recorders[id] = AnyInstance::Recorder.new(klass) + any_instance_recorder_not_found_for(id, klass) end end @@ -97,18 +105,7 @@ def proxies_of(klass) def proxy_for(object) id = id_for(object) - proxies.fetch(id) do - proxies[id] = case object - when NilClass then ProxyForNil.new(expectation_ordering) - when TestDouble then object.__build_mock_proxy(expectation_ordering) - else - if RSpec::Mocks.configuration.verify_partial_doubles? - VerifyingPartialDoubleProxy.new(object, expectation_ordering) - else - PartialDoubleProxy.new(object, expectation_ordering) - end - end - end + proxies.fetch(id) { proxy_not_found_for(id, object) } end alias ensure_registered proxy_for @@ -117,6 +114,25 @@ def registered?(object) proxies.has_key?(id_for object) end + private + + def proxy_not_found_for(id, object) + proxies[id] = case object + when NilClass then ProxyForNil.new(expectation_ordering) + when TestDouble then object.__build_mock_proxy(expectation_ordering) + else + if RSpec::Mocks.configuration.verify_partial_doubles? + VerifyingPartialDoubleProxy.new(object, expectation_ordering) + else + PartialDoubleProxy.new(object, expectation_ordering) + end + end + end + + def any_instance_recorder_not_found_for(id, klass) + any_instance_recorders[id] = AnyInstance::Recorder.new(klass) + end + if defined?(::BasicObject) && !::BasicObject.method_defined?(:__id__) # for 1.9.2 require 'securerandom' @@ -136,5 +152,34 @@ def id_for(object) end end end + + class NestedSpace < Space + def initialize(parent) + @parent = parent + super() + end + + def proxies_of(klass) + super + @parent.proxies_of(klass) + end + + def constant_mutator_for(name) + super || @parent.constant_mutator_for(name) + end + + def registered?(object) + super || @parent.registered?(object) + end + + private + + def proxy_not_found_for(id, object) + @parent.proxies[id] || super + end + + def any_instance_recorder_not_found_for(id, klass) + @parent.any_instance_recorders[id] || super + end + end end end diff --git a/spec/rspec/mocks/extensions/marshal_spec.rb b/spec/rspec/mocks/extensions/marshal_spec.rb index b24f2e097..efe304ad8 100644 --- a/spec/rspec/mocks/extensions/marshal_spec.rb +++ b/spec/rspec/mocks/extensions/marshal_spec.rb @@ -13,23 +13,6 @@ def dup end describe '#dump' do - context 'when rspec-mocks has not been fully initialized' do - def without_space - stashed_space, RSpec::Mocks.space = RSpec::Mocks.space, nil - yield - ensure - RSpec::Mocks.space = stashed_space - end - - it 'does not duplicate the object before serialization' do - obj = UndupableObject.new - without_space do - serialized = Marshal.dump(obj) - expect(Marshal.load(serialized)).to be_an(UndupableObject) - end - end - end - context 'when rspec-mocks has been fully initialized' do it 'duplicates objects with stubbed or mocked implementations before serialization' do obj = double(:foo => "bar") diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index cf73e2292..bc68b7e64 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -2,16 +2,16 @@ module RSpec::Mocks describe Space do + let(:space) { Space.new } - describe "#proxies_of(klass)" do - let(:space) { Space.new } + describe "#proxies_of(klass)" do it 'returns proxies' do space.proxy_for("") expect(space.proxies_of(String).map(&:class)).to eq([PartialDoubleProxy]) end - it 'returns only the proxies whose object is an instance of the given class' do + def create_generations grandparent_class = Class.new parent_class = Class.new(grandparent_class) child_class = Class.new(parent_class) @@ -20,12 +20,137 @@ module RSpec::Mocks parent = parent_class.new child = child_class.new + return grandparent, parent, child + end + + it 'returns only the proxies whose object is an instance of the given class' do + grandparent, parent, child = create_generations + space.proxy_for(grandparent) - parent_proxy = space.proxy_for(parent) - child_proxy = space.proxy_for(child) + parent_proxy = space.proxy_for(parent) + child_proxy = space.proxy_for(child) - expect(space.proxies_of(parent_class)).to match_array([parent_proxy, child_proxy]) + expect(space.proxies_of(parent.class)).to contain_exactly(parent_proxy, child_proxy) + end + + it 'looks in the parent space for matching proxies' do + _, parent, child = create_generations + + parent_proxy = space.proxy_for(parent) + subspace = space.new_scope + child_proxy = subspace.proxy_for(child) + + expect(subspace.proxies_of(parent.class)).to contain_exactly(parent_proxy, child_proxy) + end + end + + it 'tracks proxies in parent and child space separately' do + proxy1 = space.proxy_for(Object.new) + subspace = space.new_scope + proxy2 = subspace.proxy_for(Object.new) + + expect(space.proxies.values).to include(proxy1) + expect(space.proxies.values).not_to include(proxy2) + + expect(subspace.proxies.values).to include(proxy2) + expect(subspace.proxies.values).not_to include(proxy1) + end + + [:ensure_registered, :proxy_for].each do |method| + describe "##{method}" do + define_method :get_proxy do |space, object| + space.__send__(method, object) + end + + it 'returns the proxy for the given object' do + obj1 = Object.new + obj2 = Object.new + + expect(get_proxy(space, obj1)).to equal(get_proxy(space, obj1)) + expect(get_proxy(space, obj2)).to equal(get_proxy(space, obj2)) + expect(get_proxy(space, obj1)).not_to equal(get_proxy(space, obj2)) + end + + it 'can stil return a proxy from a parent context' do + proxy = get_proxy(space, Object) + subspace = space.new_scope + + expect(get_proxy(subspace, Object)).to equal(proxy) + end + + it "does not store a parent's proxy in the child space" do + get_proxy(space, Object) + subspace = space.new_scope + + expect { + get_proxy(subspace, Object) + }.not_to change { subspace.proxies }.from({}) + end + end + end + + describe "#registered?" do + it 'returns true if registered in this space' do + space.ensure_registered(Object) + expect(space).to be_registered(Object) + end + + it 'returns true if registered in a parent space' do + space.ensure_registered(Object) + expect(space.new_scope).to be_registered(Object) + end + + it 'returns false if not registered in this or a parent space' do + expect(space.new_scope).not_to be_registered(Object) + end + end + + describe "#constant_mutator_for" do + it 'returns the mutator for the given const name' do + space = RSpec::Mocks.space + stub_const("Foo", 3) + stub_const("Bar", 4) + + expect(space.constant_mutator_for("Foo")).to equal(space.constant_mutator_for("Foo")) + expect(space.constant_mutator_for("Bar")).to equal(space.constant_mutator_for("Bar")) + expect(space.constant_mutator_for("Foo")).not_to equal(space.constant_mutator_for("Bar")) + end + + it 'can stil return a mutator from a parent context' do + space = RSpec::Mocks.space + + stub_const("Foo", 3) + mutator = space.constant_mutator_for("Foo") + + in_new_space_scope do + subspace = RSpec::Mocks.space + expect(subspace.constant_mutator_for("Foo")).to equal(mutator) + end + end + end + + describe "#any_instance_recorder_for" do + it 'returns the recorder for the given class' do + expect(space.any_instance_recorder_for(String)).to equal(space.any_instance_recorder_for(String)) + expect(space.any_instance_recorder_for(Symbol)).to equal(space.any_instance_recorder_for(Symbol)) + expect(space.any_instance_recorder_for(String)).not_to equal(space.any_instance_recorder_for(Symbol)) + end + + it 'can stil return a recorder from a parent context' do + recorder = space.any_instance_recorder_for(String) + subspace = space.new_scope + + expect(subspace.any_instance_recorder_for(String)).to equal(recorder) + end + + it "does not store a parent's proxy in the child space" do + space.any_instance_recorder_for(String) + subspace = space.new_scope + + expect { + subspace.any_instance_recorder_for(String) + }.not_to change { subspace.any_instance_recorders }.from({}) end end @@ -41,5 +166,11 @@ module RSpec::Mocks }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /Diff/) end + def in_new_space_scope + RSpec::Mocks.setup + yield + ensure + RSpec::Mocks.teardown + end end end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 992c71fb7..22f41b84e 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -1,25 +1,51 @@ require "spec_helper" describe RSpec::Mocks do - describe "::verify" do + describe ".verify" do it "delegates to the space" do foo = double foo.should_receive(:bar) expect do - RSpec::Mocks::verify + RSpec::Mocks.verify end.to raise_error(RSpec::Mocks::MockExpectationError) RSpec::Mocks.teardown # so the mocks aren't re-verified after this example end end - describe "::teardown" do + describe ".teardown" do it "resets method stubs" do string = "foo" allow(string).to receive(:bar) RSpec::Mocks.teardown expect { string.bar }.to raise_error(NoMethodError) end + + it "does not put rspec-mocks into an inconsistent state when called extra times" do + RSpec::Mocks.teardown + RSpec::Mocks.teardown + RSpec::Mocks.teardown + + string = "foo" + expect { allow(string).to receive(:bar) }.to raise_error(RSpec::Mocks::OutsideOfExampleError) + + RSpec::Mocks.setup + allow(string).to receive(:bar).and_return(:baz) + expect(string.bar).to eq(:baz) + end + end + + describe ".setup" do + it 'starts a new space scope that is later removed by .teardown' do + old_space = RSpec::Mocks.space + RSpec::Mocks.setup + + new_space = RSpec::Mocks.space + expect(new_space).not_to equal(old_space) + + RSpec::Mocks.teardown + expect(RSpec::Mocks.space).to equal(old_space) + end end describe ".configuration" do From 3dcef6d4499e83cc64c970f5b17b68c9cc6e83ae Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Jan 2014 10:03:18 -0800 Subject: [PATCH 0414/1179] Add RSpec::Mocks.with_temporary_scope. Closes #240. --- Changelog.md | 3 + lib/rspec/mocks.rb | 11 ++++ spec/rspec/mocks_spec.rb | 123 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/Changelog.md b/Changelog.md index 7d03d6634..8b325f891 100644 --- a/Changelog.md +++ b/Changelog.md @@ -48,6 +48,9 @@ Enhancements: * Change argument matchers to use `===` as their primary matching protocol, as their semantics mirror that of a case or rescue statement (which uses `===` for matching). (Myron Marston) +* Add `RSpec::Mocks.with_temporary_scope`, which allows you to create + temporary rspec-mocks scopes in arbitrary places (such as a + `before(:all)` hook). (Myron Marston) ### 3.0.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 69fd1d66b..99aa995a9 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -66,6 +66,17 @@ def self.expect_message(subject, message, opts={}, &block) space.proxy_for(subject).add_message_expectation(orig_caller, message, opts, &block) end + def self.with_temporary_scope + setup + + begin + yield + verify + ensure + teardown + end + end + class << self; attr_reader :space; end @space_stack = [] @root_space = @space = RSpec::Mocks::RootSpace.new diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 22f41b84e..44bb81fc6 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -55,6 +55,129 @@ end end + describe ".with_temporary_scope" do + context "in a before(:all) with a stubbed double" do + before(:all) do + RSpec::Mocks.with_temporary_scope do + @calculator = double + allow(@calculator).to receive(:add) { |a, b| a + b } + @sum = @calculator.add(3, 4) + end + + capture_error { @calculator.add(1, 10) } + end + + it 'allows the stubbed double to be used' do + expect(@sum).to eq(7) + end + + it 'does not allow the double to be used in the examples' do + expect { + @calculator.add(1, 2) + }.to raise_error(RSpec::Mocks::ExpiredTestDoubleError) + end + + it 'does not allow the double to be used after the scope in before(:all)' do + expect(@error).to be_a(RSpec::Mocks::OutsideOfExampleError) + end + end + + context "in a before(:all) with a stubbed const" do + before(:all) do + RSpec::Mocks.with_temporary_scope do + stub_const("ValueX", 3) + stub_const("ValueY", 4) + @sum = ValueX + ValueY + end + + capture_error { ValueX + ValueY } + end + + it 'allows the stubbed constants to be used' do + expect(@sum).to eq(7) + end + + it 'does not allow the stubbed constants to be used in the examples' do + expect(defined?(ValueX)).to be_falsey + expect(defined?(ValueY)).to be_falsey + end + + it 'does not allow the stubbed constants to be used after the scope in before(:all)' do + expect(@error).to be_a(NameError) + expect(@error.message).to include("ValueX") + end + end + + context "in a before(:all) with a unmet mock expectation" do + before(:all) do + capture_error do + RSpec::Mocks.with_temporary_scope do + calculator = double + expect(calculator).to receive(:add) + end + end + end + + it 'fails with a mock expectation error' do + expect(@error).to be_a(RSpec::Mocks::MockExpectationError) + end + end + + context "in a before(:all) with an any_instance stub" do + before(:all) do + RSpec::Mocks.with_temporary_scope do + allow_any_instance_of(String).to receive(:sum_with) { |val, x| val + x } + @sum = "foo".sum_with("bar") + end + + capture_error { "you".sum_with("me") } + end + + it 'allows the stub to be used' do + expect(@sum).to eq("foobar") + end + + it 'does not allow the double to be used in the examples' do + expect { + "foo".sum_with("baz") + }.to raise_error(NameError, /sum_with/) + end + + it 'does not allow the double to be used after the scope in before(:all)' do + expect(@error).to be_a(NameError) + expect(@error.message).to include("sum_with") + end + end + + it 'tears down even if an error occurs' do + foo = Object.new + + expect { + RSpec::Mocks.with_temporary_scope do + allow(foo).to receive(:bar) + raise "boom" + end + }.to raise_error("boom") + + expect(foo).not_to respond_to(:bar) + end + + it 'does not verify if an error occurs before the block completes' do + expect { + RSpec::Mocks.with_temporary_scope do + foo = Object.new + expect(foo).to receive(:bar) + raise "boom" + end + }.to raise_error("boom") # rather than MockExpectationError + end + + def capture_error + yield + rescue Exception => @error + end + end + context "when there is a `let` declaration that overrides an argument matcher" do let(:boolean) { :from_let } From 98f065eb44beb4cf60b0970aa619a08cb86e21f6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Jan 2014 20:55:26 -0800 Subject: [PATCH 0415/1179] Clean up expectation ordering. - It was duplicated in Proxy. - Makes more sense to initialize it in Space init. --- lib/rspec/mocks/proxy.rb | 9 ++++----- lib/rspec/mocks/space.rb | 17 +++++++---------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index f3fb3d285..a478b8556 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -14,7 +14,6 @@ def initialize(object, order_group, name=nil, options={}) @order_group = order_group @name = name @error_generator = ErrorGenerator.new(object, name) - @expectation_ordering = RSpec::Mocks::space.expectation_ordering @messages_received = [] @options = options @null_object = false @@ -47,12 +46,12 @@ def add_message_expectation(location, method_name, opts={}, &block) meth_double = method_double_for(method_name) if null_object? && !block - meth_double.add_default_stub(@error_generator, @expectation_ordering, location, opts) do + meth_double.add_default_stub(@error_generator, @order_group, location, opts) do @object end end - meth_double.add_expectation @error_generator, @expectation_ordering, location, opts, &block + meth_double.add_expectation @error_generator, @order_group, location, opts, &block end # @private @@ -66,7 +65,7 @@ def build_expectation(method_name) meth_double.build_expectation( @error_generator, - @expectation_ordering + @order_group ) end @@ -103,7 +102,7 @@ def check_for_unexpected_arguments(expectation) # @private def add_stub(location, method_name, opts={}, &implementation) - method_double_for(method_name).add_stub @error_generator, @expectation_ordering, location, opts, &implementation + method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation end # @private diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index e40d8ef78..4a447b2df 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -41,12 +41,13 @@ def raise_lifecycle_message # @api private class Space - attr_reader :proxies, :any_instance_recorders + attr_reader :proxies, :any_instance_recorders, :expectation_ordering def initialize @proxies = {} @any_instance_recorders = {} @constant_mutators = [] + @expectation_ordering = OrderGroup.new end def new_scope @@ -72,14 +73,10 @@ def reset_all proxies.clear any_instance_recorders.clear - expectation_ordering.clear + @expectation_ordering.clear @constant_mutators.clear end - def expectation_ordering - @expectation_ordering ||= OrderGroup.new - end - def register_constant_mutator(mutator) @constant_mutators << mutator end @@ -118,13 +115,13 @@ def registered?(object) def proxy_not_found_for(id, object) proxies[id] = case object - when NilClass then ProxyForNil.new(expectation_ordering) - when TestDouble then object.__build_mock_proxy(expectation_ordering) + when NilClass then ProxyForNil.new(@expectation_ordering) + when TestDouble then object.__build_mock_proxy(@expectation_ordering) else if RSpec::Mocks.configuration.verify_partial_doubles? - VerifyingPartialDoubleProxy.new(object, expectation_ordering) + VerifyingPartialDoubleProxy.new(object, @expectation_ordering) else - PartialDoubleProxy.new(object, expectation_ordering) + PartialDoubleProxy.new(object, @expectation_ordering) end end end From d2b335e29420344d99b4d2fad90b68ef3188367e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Jan 2014 20:59:44 -0800 Subject: [PATCH 0416/1179] Simplify spec. rspec-mocks lacks the necessary sandboxing to safely define and run examples from within examples. rspec-core has and uses this but it's not exposed for use here. Instead, we can just trigger the teardown/setup that happens between examples. This fixes some rspec-mocks space leakage that was happening. --- .../mocks/nil_expectation_warning_spec.rb | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index 54c8f93a2..9e020185e 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -1,14 +1,5 @@ require 'spec_helper' -def remove_last_describe_from_world - RSpec::world.example_groups.pop -end - -def empty_example_group - RSpec::Core::ExampleGroup.describe(Object, 'Empty Behaviour Group') { } - remove_last_describe_from_world -end - module RSpec module Mocks describe "an expectation set on nil" do @@ -44,18 +35,12 @@ module Mocks describe "#allow_message_expectations_on_nil" do it "does not affect subsequent examples" do - example_group = ::RSpec::Core::ExampleGroup.describe - reporter = ::RSpec.configuration.reporter - example_group.it("when called in one example that doesn't end up setting an expectation on nil") do - allow_message_expectations_on_nil - end - example_group.it("should not effect the next example ran") do - Kernel.should_receive(:warn) - nil.should_receive(:foo) - nil.foo - end - - expect(example_group.run reporter).to eq true + allow_message_expectations_on_nil + RSpec::Mocks.teardown + RSpec::Mocks.setup + Kernel.should_receive(:warn) + nil.should_receive(:foo) + nil.foo end it 'doesnt error when marshalled' do From 90cf55073ad8d21ceeb5cdc8eb58f6ebd51f9ceb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Jan 2014 21:45:29 -0800 Subject: [PATCH 0417/1179] Move mock_space_spec specs into space_spec.rb Not sure how we wound up with two separate files, anyway. --- spec/rspec/mocks/mock_space_spec.rb | 113 ---------------------------- spec/rspec/mocks/space_spec.rb | 92 ++++++++++++++++++++++ 2 files changed, 92 insertions(+), 113 deletions(-) delete mode 100644 spec/rspec/mocks/mock_space_spec.rb diff --git a/spec/rspec/mocks/mock_space_spec.rb b/spec/rspec/mocks/mock_space_spec.rb deleted file mode 100644 index bd39ef97c..000000000 --- a/spec/rspec/mocks/mock_space_spec.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'spec_helper' -require 'rspec/mocks' - -module RSpec - module Mocks - describe Space do - let(:space) { RSpec::Mocks::Space.new } - let(:dbl_1) { Object.new } - let(:dbl_2) { Object.new } - - before do - space.ensure_registered(dbl_1) - space.ensure_registered(dbl_2) - end - - it "verifies all mocks within" do - verifies = [] - - space.proxy_for(dbl_1).stub(:verify) { verifies << :dbl_1 } - space.proxy_for(dbl_2).stub(:verify) { verifies << :dbl_2 } - - space.verify_all - - expect(verifies).to match_array([:dbl_1, :dbl_2]) - end - - def define_singleton_method_on_recorder_for(klass, name, &block) - recorder = space.any_instance_recorder_for(klass) - (class << recorder; self; end).send(:define_method, name, &block) - end - - it 'verifies all any_instance recorders within' do - klass_1, klass_2 = Class.new, Class.new - - verifies = [] - - # We can't `stub` a method on the recorder because it defines its own `stub`... - define_singleton_method_on_recorder_for(klass_1, :verify) { verifies << :klass_1 } - define_singleton_method_on_recorder_for(klass_2, :verify) { verifies << :klass_2 } - - space.verify_all - - expect(verifies).to match_array([:klass_1, :klass_2]) - end - - it "resets all mocks within" do - resets = [] - - space.proxy_for(dbl_1).stub(:reset) { resets << :dbl_1 } - space.proxy_for(dbl_2).stub(:reset) { resets << :dbl_2 } - - space.reset_all - - expect(resets).to match_array([:dbl_1, :dbl_2]) - end - - it "does not leak mock proxies between examples" do - expect { - space.reset_all - }.to change { space.proxies.size }.to(0) - end - - it 'does not leak any instance recorders between examples' do - space.any_instance_recorder_for(Class.new) - expect { - space.reset_all - }.to change { space.any_instance_recorders.size }.to(0) - end - - it "resets the ordering" do - space.expectation_ordering.register :some_expectation - - expect { - space.reset_all - }.to change { space.expectation_ordering.empty? }.from(false).to(true) - end - - it "only adds an instance once" do - m1 = double("mock1") - - expect { - space.ensure_registered(m1) - }.to change { space.proxies } - - expect { - space.ensure_registered(m1) - }.not_to change { space.proxies } - end - - it 'returns a consistent any_instance_recorder for a given class' do - klass_1, klass_2 = Class.new, Class.new - - r1 = space.any_instance_recorder_for(klass_1) - r2 = space.any_instance_recorder_for(klass_1) - r3 = space.any_instance_recorder_for(klass_2) - - expect(r1).to be(r2) - expect(r1).not_to be(r3) - end - - it 'removes an any_instance_recorder when requested' do - klass = Class.new - - space.any_instance_recorder_for(klass) - - expect { - space.remove_any_instance_recorder_for(klass) - }.to change { space.any_instance_recorders.size }.by(-1) - end - end - end -end - diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index bc68b7e64..514f5db04 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -3,7 +3,77 @@ module RSpec::Mocks describe Space do let(:space) { Space.new } + let(:dbl_1) { Object.new } + let(:dbl_2) { Object.new } + describe "#verify_all" do + it "verifies all mocks within" do + verifies = [] + + space.proxy_for(dbl_1).stub(:verify) { verifies << :dbl_1 } + space.proxy_for(dbl_2).stub(:verify) { verifies << :dbl_2 } + + space.verify_all + + expect(verifies).to match_array([:dbl_1, :dbl_2]) + end + + def define_singleton_method_on_recorder_for(klass, name, &block) + recorder = space.any_instance_recorder_for(klass) + (class << recorder; self; end).send(:define_method, name, &block) + end + + it 'verifies all any_instance recorders within' do + klass_1, klass_2 = Class.new, Class.new + + verifies = [] + + # We can't `stub` a method on the recorder because it defines its own `stub`... + define_singleton_method_on_recorder_for(klass_1, :verify) { verifies << :klass_1 } + define_singleton_method_on_recorder_for(klass_2, :verify) { verifies << :klass_2 } + + space.verify_all + + expect(verifies).to match_array([:klass_1, :klass_2]) + end + end + + describe "#reset_all" do + it "resets all mocks within" do + resets = [] + + space.proxy_for(dbl_1).stub(:reset) { resets << :dbl_1 } + space.proxy_for(dbl_2).stub(:reset) { resets << :dbl_2 } + + space.reset_all + + expect(resets).to match_array([:dbl_1, :dbl_2]) + end + + it "does not leak mock proxies between examples" do + space.ensure_registered(dbl_1) + space.ensure_registered(dbl_2) + + expect { + space.reset_all + }.to change { space.proxies.size }.to(0) + end + + it 'does not leak any instance recorders between examples' do + space.any_instance_recorder_for(Class.new) + expect { + space.reset_all + }.to change { space.any_instance_recorders.size }.to(0) + end + + it "resets the ordering" do + space.expectation_ordering.register :some_expectation + + expect { + space.reset_all + }.to change { space.expectation_ordering.empty? }.from(false).to(true) + end + end describe "#proxies_of(klass)" do it 'returns proxies' do @@ -57,6 +127,18 @@ def create_generations expect(subspace.proxies.values).not_to include(proxy1) end + it "only adds an instance once" do + m1 = double("mock1") + + expect { + space.ensure_registered(m1) + }.to change { space.proxies } + + expect { + space.ensure_registered(m1) + }.not_to change { space.proxies } + end + [:ensure_registered, :proxy_for].each do |method| describe "##{method}" do define_method :get_proxy do |space, object| @@ -166,6 +248,16 @@ def create_generations }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /Diff/) end + it 'removes an any_instance_recorder when requested' do + klass = Class.new + + space.any_instance_recorder_for(klass) + + expect { + space.remove_any_instance_recorder_for(klass) + }.to change { space.any_instance_recorders.size }.by(-1) + end + def in_new_space_scope RSpec::Mocks.setup yield From fab521089c3ed85782cd595e6448bd48fe5ca5c7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Jan 2014 21:47:07 -0800 Subject: [PATCH 0418/1179] Remove unnecessary collection clearing. Since we now use a new space instance per example, we don't need to clear its collections when resetting. Before this was necessary because we kept a space instance that we would keep using for the lifetime of the process. Constant mutators weren't being reset idempotently, so I had to tweak them a bit. --- lib/rspec/mocks/mutate_const.rb | 6 ++++++ lib/rspec/mocks/space.rb | 21 ++++---------------- spec/rspec/mocks/space_spec.rb | 24 ----------------------- spec/rspec/mocks/verifying_double_spec.rb | 3 ++- 4 files changed, 12 insertions(+), 42 deletions(-) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index 73db1ecc4..ce12007f5 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -207,6 +207,7 @@ def initialize(full_constant_name, mutated_value, transfer_nested_constants) @transfer_nested_constants = transfer_nested_constants @context_parts = @full_constant_name.split('::') @const_name = @context_parts.pop + @reset_performed = false end def to_constant @@ -215,6 +216,11 @@ def to_constant const end + + def idempotently_reset + reset unless @reset_performed + @reset_performed = true + end end # Hides a defined constant for the duration of an example. diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 4a447b2df..ec2c65ac0 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -55,26 +55,13 @@ def new_scope end def verify_all - proxies.each_value do |object| - object.verify - end - - any_instance_recorders.each_value do |recorder| - recorder.verify - end + proxies.each_value { |proxy| proxy.verify } + any_instance_recorders.each_value { |recorder| recorder.verify } end def reset_all - @constant_mutators.reverse.each { |mut| mut.reset } - - proxies.each_value do |object| - object.reset - end - - proxies.clear - any_instance_recorders.clear - @expectation_ordering.clear - @constant_mutators.clear + proxies.each_value { |proxy| proxy.reset } + @constant_mutators.reverse.each { |mut| mut.idempotently_reset } end def register_constant_mutator(mutator) diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index 514f5db04..b37c144b4 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -49,30 +49,6 @@ def define_singleton_method_on_recorder_for(klass, name, &block) expect(resets).to match_array([:dbl_1, :dbl_2]) end - - it "does not leak mock proxies between examples" do - space.ensure_registered(dbl_1) - space.ensure_registered(dbl_2) - - expect { - space.reset_all - }.to change { space.proxies.size }.to(0) - end - - it 'does not leak any instance recorders between examples' do - space.any_instance_recorder_for(Class.new) - expect { - space.reset_all - }.to change { space.any_instance_recorders.size }.to(0) - end - - it "resets the ordering" do - space.expectation_ordering.register :some_expectation - - expect { - space.reset_all - }.to change { space.expectation_ordering.empty? }.from(false).to(true) - end end describe "#proxies_of(klass)" do diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 10cd05108..12823eb2e 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -373,7 +373,8 @@ module Mocks expect(object).to receive(:defined_class_method) expect(LoadedClass).to eq(object) - ::RSpec::Mocks.space.reset_all + ::RSpec::Mocks.teardown + ::RSpec::Mocks.setup expect(LoadedClass).to eq(original) end From 3ab7925b0af315cbcc526b504d63f24dd8971ae0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Jan 2014 22:13:11 -0800 Subject: [PATCH 0419/1179] Add changelog entry for test-double self-destruction. [ci skip] --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 8b325f891..4eda0b457 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,10 @@ Breaking Changes for 3.0.0: * Remove the `host` argument of `RSpec::Mocks.setup`. Instead `RSpec::Mocks::ExampleMethods` should be included directly in the scope where RSpec's mocking capabilities are used. (Sam Phippen) +* Make test doubles raise errors if you attempt to use them after they + get reset, to help surface issues when you accidentally retain + references to test doubles and attempt to reuse them in another + example. (Myron Marston) Bug Fixes: From c08a40881eeebbd76c0982208a0bb9e156cc48af Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 7 Jan 2014 08:18:14 -0800 Subject: [PATCH 0420/1179] Indent `private` at same level as class. This is our preferred convention. --- lib/rspec/mocks/proxy.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index a478b8556..76098ccd1 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -194,7 +194,7 @@ def visibility_for(method_name) :public end - private + private def method_double_for(message) @method_doubles[message.to_sym] @@ -315,7 +315,7 @@ def add_stub(location, method_name, opts={}, &implementation) super end - private + private def warn method_name source = CallerFilter.first_non_rspec_line From 4cf7d25502c6c2099df9a148c7f91ca39097f45b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Jan 2014 09:59:07 -0800 Subject: [PATCH 0421/1179] Fix indentation. --- features/spies/spy_pure_mock_method.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/spies/spy_pure_mock_method.feature b/features/spies/spy_pure_mock_method.feature index b1bda01e6..6b1f3209f 100644 --- a/features/spies/spy_pure_mock_method.feature +++ b/features/spies/spy_pure_mock_method.feature @@ -66,7 +66,7 @@ Feature: Spy on a stubbed method on a pure mock Given a file named "spy_message_spec.rb" with: """ruby describe "have_received" do - subject(:invitation) { double('invitation', :deliver => true) } + subject(:invitation) { double('invitation', :deliver => true) } before { invitation.deliver } it { should have_received(:deliver) } From 949e2d6bf38bade332a0cc9839db7533b662b0a6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Jan 2014 10:00:34 -0800 Subject: [PATCH 0422/1179] Add spec for `have_received` lifecycle issue. --- spec/rspec/mocks/matchers/have_received_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 8fc3030a6..42a9b41bd 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -132,6 +132,13 @@ module Mocks expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' end + it 'can generate a description after mocks have been torn down (e.g. when rspec-core requests it)' do + matcher = have_received(:expected_method).with(:expected_args).once + matcher.matches?(double(:expected_method => 1)) + RSpec::Mocks.teardown + expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' + end + context "counts" do let(:dbl) { double(:expected_method => nil) } From 74e79909474b6ac419909366ca1d263c99c2c10c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 10 Jan 2014 17:27:08 -0800 Subject: [PATCH 0423/1179] Fix null_object double visibility bug. The error being raised had the word "private" in it only because the method itself had that in its name, but this did not indicate it was raising the normal "private method called" error raised by ruby. --- lib/rspec/mocks/error_generator.rb | 8 ++++++++ lib/rspec/mocks/verifying_double.rb | 2 +- lib/rspec/mocks/verifying_proxy.rb | 9 +++++++++ spec/rspec/mocks/verifying_double_spec.rb | 11 ++++++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 66c87be8f..b2f03a52a 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -73,6 +73,13 @@ def raise_unimplemented_error(doubled_module, method_name) ] end + # @private + def raise_non_public_error(method_name, visibility) + raise NoMethodError, "%s method `%s' called on %s" % [ + visibility, method_name, intro + ] + end + # @private def raise_arity_error(calculator, actual) __raise "Wrong number of arguments. Expected %s, got %s." % [ @@ -81,6 +88,7 @@ def raise_arity_error(calculator, actual) ] end + # @private def raise_expired_test_double_error raise ExpiredTestDoubleError, "#{intro} was originally created in one example but has leaked into " + diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 8d1a8bdbb..e6b3f5c3a 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -22,7 +22,7 @@ def method_missing(message, *args, &block) # Null object conditional is an optimization. If not a null object, # validity of method expectations will have been checked at definition # time. - __mock_proxy.ensure_implemented(message) if null_object? + __mock_proxy.ensure_publicly_implemented(message, self) if null_object? super end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 254eb67cc..882590409 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -29,6 +29,15 @@ def ensure_implemented(method_name) ) end end + + def ensure_publicly_implemented(method_name, object) + ensure_implemented(method_name) + visibility = method_reference[method_name].visibility + + unless visibility == :public + @error_generator.raise_non_public_error(method_name, visibility) + end + end end # A verifying proxy mostly acts like a normal proxy, except that it diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 12823eb2e..b6b7edd85 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -497,7 +497,7 @@ def preserves_visibility(method_name, visibility) expect { # send bypasses visbility, so we use eval instead. eval("double.#{method_name}") - }.to raise_error(NoMethodError, /#{visibility}/) + }.to raise_error(NoMethodError, a_message_indicating_visibility_violation(method_name, visibility)) unless double.null_object? # Null object doubles use `method_missing` and so the singleton class @@ -506,6 +506,15 @@ def preserves_visibility(method_name, visibility) expect(singleton_class.send("#{visibility}_method_defined?", method_name)).to be true end end + + RSpec::Matchers.define :a_message_indicating_visibility_violation do |method_name, visibility| + match do |msg| + # This should NOT Be just `msg.match(visibility)` because the method being called + # has the visibility name in it. We want to ensure it's a message that ruby is + # stating is of the given visibility. + msg.match("#{visibility} ") && msg.match(method_name.to_s) + end + end end end end From 37300da87a670f7dcd0be61f9dc214998a5a24c2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 10 Jan 2014 18:00:19 -0800 Subject: [PATCH 0424/1179] Allow null object verified doubles to receive private methods using `send`. This mirrors the behavior of the real object. --- lib/rspec/mocks/test_double.rb | 12 +++++----- lib/rspec/mocks/verifying_double.rb | 27 ++++++++++++++++++++++- spec/rspec/mocks/null_object_mock_spec.rb | 10 +++++++++ spec/rspec/mocks/verifying_double_spec.rb | 9 ++++++-- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index cc1200c9f..d1e6f808e 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -86,12 +86,6 @@ def method_missing(message, *args, &block) proxy = __mock_proxy proxy.record_message_received(message, *args, &block) - # Defined private and protected methods will still trigger `method_missing` - # when called publicly. We want ruby's method visibility error to get raised, - # so we simply delegate to `super` in that case. - visibility = proxy.visibility_for(message) - return super if visibility == :private || visibility == :protected - if proxy.null_object? case message when :to_int then return 0 @@ -100,6 +94,12 @@ def method_missing(message, *args, &block) end end + # Defined private and protected methods will still trigger `method_missing` + # when called publicly. We want ruby's method visibility error to get raised, + # so we simply delegate to `super` in that case. + visibility = proxy.visibility_for(message) + return super if visibility == :private || visibility == :protected + # Required wrapping doubles in an Array on Ruby 1.9.2 raise NoMethodError if [:to_a, :to_ary].include? message proxy.raise_unexpected_message_error(message, *args) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index e6b3f5c3a..553cc4177 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -1,4 +1,5 @@ require 'rspec/mocks/verifying_proxy' +require 'stringio' module RSpec module Mocks @@ -22,8 +23,32 @@ def method_missing(message, *args, &block) # Null object conditional is an optimization. If not a null object, # validity of method expectations will have been checked at definition # time. - __mock_proxy.ensure_publicly_implemented(message, self) if null_object? + if null_object? + if @__sending_message == message + __mock_proxy.ensure_implemented(message) + else + __mock_proxy.ensure_publicly_implemented(message, self) + end + end + + super + end + + def send(name, *args, &block) + @__sending_message = name + super + ensure + @__sending_message = nil + end + + # Redefining `__send__` causes ruby to issue a warning. + old, $stderr = $stderr, StringIO.new + alias __send__ send + $stderr = old + + def __initialize_as_test_double(*args) super + @__sending_message = nil end end diff --git a/spec/rspec/mocks/null_object_mock_spec.rb b/spec/rspec/mocks/null_object_mock_spec.rb index e6b571a53..01ca4851c 100644 --- a/spec/rspec/mocks/null_object_mock_spec.rb +++ b/spec/rspec/mocks/null_object_mock_spec.rb @@ -69,6 +69,16 @@ module Mocks verify @double end + it 'allows unexpected message sends using `send`' do + val = @double.send(:foo).send(:bar) + expect(val).to equal(@double) + end + + it 'allows unexpected message sends using `send`' do + val = @double.__send__(:foo).__send__(:bar) + expect(val).to equal(@double) + end + it "allows expected message with different args first" do @double.should_receive(:message).with(:expected_arg) @double.message(:unexpected_arg) diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index b6b7edd85..5a3e78a77 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -149,7 +149,7 @@ module Mocks it "preserves #{visibility} visibility when expecting a #{visibility} method" do preserves_visibility(method_name, visibility) do instance_double('LoadedClass').tap do |o| - expect(o).to receive(method_name) + expect(o).to receive(method_name).at_least(:once) o.send(method_name) # to satisfy the expectation end end @@ -204,6 +204,8 @@ module Mocks it 'only allows defined methods' do expect(o.defined_instance_method).to eq(o) prevents { o.undefined_method } + prevents { o.send(:undefined_method) } + prevents { o.__send__(:undefined_method) } end it 'reports what public messages it responds to accurately' do @@ -319,7 +321,7 @@ module Mocks it "preserves #{visibility} visibility when expecting a #{visibility} method" do preserves_visibility(method_name, visibility) do class_double('LoadedClass').tap do |o| - expect(o).to receive(method_name) + expect(o).to receive(method_name).at_least(:once) o.send(method_name) # to satisfy the expectation end end @@ -499,6 +501,9 @@ def preserves_visibility(method_name, visibility) eval("double.#{method_name}") }.to raise_error(NoMethodError, a_message_indicating_visibility_violation(method_name, visibility)) + expect { double.send(method_name) }.not_to raise_error + expect { double.__send__(method_name) }.not_to raise_error + unless double.null_object? # Null object doubles use `method_missing` and so the singleton class # doesn't know what methods are defined. From 50f4d56aa6fc6d1b6a498ed44322a6613560bb9e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 10 Jan 2014 20:37:34 -0800 Subject: [PATCH 0425/1179] Fix 1.8.7. I'm not sure why, but aliasing `__send__` to `send` on 1.8.7 doesn't work correctly. Defining the alias manually does, for some reason. While we're at it, it makes more sense to define `__send__` as primary and `send` as secondary, as it's more likely users will stub `send` (e.g. for an `email` object). --- lib/rspec/mocks/verifying_double.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 553cc4177..9baeac1d5 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -34,18 +34,20 @@ def method_missing(message, *args, &block) super end - def send(name, *args, &block) + # Redefining `__send__` causes ruby to issue a warning. + old, $stderr = $stderr, StringIO.new + def __send__(name, *args, &block) @__sending_message = name super ensure @__sending_message = nil end - - # Redefining `__send__` causes ruby to issue a warning. - old, $stderr = $stderr, StringIO.new - alias __send__ send $stderr = old + def send(name, *args, &block) + __send__(name, *args, &block) + end + def __initialize_as_test_double(*args) super @__sending_message = nil From 877659477a9afac611918f20c8510b82228bd4f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 10 Jan 2014 20:42:07 -0800 Subject: [PATCH 0426/1179] Add specs to ensure `send` can be stubbed. --- spec/rspec/mocks/double_spec.rb | 6 ++++++ spec/rspec/mocks/verifying_double_spec.rb | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 94d6d190b..28b58e192 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -50,6 +50,12 @@ module Mocks expect(dbl.foo = "bar").to eq("bar") end + it 'allows `send` to be stubbed' do + dbl = double + allow(dbl).to receive(:send).and_return("received") + expect(dbl.send(:some_msg)).to eq("received") + end + context "after it has been torn down" do let(:dbl) { double } diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 5a3e78a77..9b9937b90 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -34,6 +34,9 @@ def defined_private_class_method def defined_instance_method end + def send(*) + end + def respond_to?(method_name, include_all = false) return true if method_name == :dynamic_instance_method super @@ -122,6 +125,12 @@ module Mocks prevents { o.should_receive(:defined_class_method) } end + it 'allows `send` to be stubbed if it is defined on the class' do + o = instance_double('LoadedClass') + allow(o).to receive(:send).and_return("received") + expect(o.send(:msg)).to eq("received") + end + describe "method visibility" do shared_examples_for "preserves method visibility" do |visibility| method_name = :"defined_#{visibility}_method" From c601d5810a88fa235b302cc1e3b28bcdf0a7c430 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 10 Jan 2014 21:37:29 -0800 Subject: [PATCH 0427/1179] Work around JRuby bug. --- lib/rspec/mocks/test_double.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index d1e6f808e..a2c2b00e6 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -97,8 +97,15 @@ def method_missing(message, *args, &block) # Defined private and protected methods will still trigger `method_missing` # when called publicly. We want ruby's method visibility error to get raised, # so we simply delegate to `super` in that case. + # ...well, we would delegate to `super`, but there's a JRuby + # bug, so we raise our own visibility error instead: + # https://github.com/jruby/jruby/issues/1398 visibility = proxy.visibility_for(message) - return super if visibility == :private || visibility == :protected + if visibility == :private || visibility == :protected + ErrorGenerator.new(self, @name).raise_non_public_error( + message, visibility + ) + end # Required wrapping doubles in an Array on Ruby 1.9.2 raise NoMethodError if [:to_a, :to_ary].include? message From ed10867f0d1c9b80eba0e8324367ca5fe3ee2d9a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 22 Jan 2014 23:06:43 -0800 Subject: [PATCH 0428/1179] Delete unneeded script. [ci skip] --- script/download-ruby-debug-19-dependencies | 4 ---- 1 file changed, 4 deletions(-) delete mode 100755 script/download-ruby-debug-19-dependencies diff --git a/script/download-ruby-debug-19-dependencies b/script/download-ruby-debug-19-dependencies deleted file mode 100755 index fee47e5f3..000000000 --- a/script/download-ruby-debug-19-dependencies +++ /dev/null @@ -1,4 +0,0 @@ -mkdir -p vendor/cache -cd vendor/cache -wget http://rubyforge.org/frs/download.php/75414/linecache19-0.5.13.gem -wget http://rubyforge.org/frs/download.php/75415/ruby-debug-base19-0.11.26.gem From c81e9f983faa6eb229f0adacf8d132e002f4d87e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 22 Jan 2014 22:21:26 -0800 Subject: [PATCH 0429/1179] Update test-all script so it builds against core and expectations. If we break those gems we want to be notified about it. --- maintenence-branch | 1 + script/test_all | 61 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 maintenence-branch diff --git a/maintenence-branch b/maintenence-branch new file mode 100644 index 000000000..1f7391f92 --- /dev/null +++ b/maintenence-branch @@ -0,0 +1 @@ +master diff --git a/script/test_all b/script/test_all index 30c35d14a..ebbb3224e 100755 --- a/script/test_all +++ b/script/test_all @@ -13,13 +13,8 @@ function is_jruby() { # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived -# force jRuby to use client mode JVM or a compilation mode thats as close as possible, -# idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time -export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' - echo "Running all..." - -bin/rspec spec --format progress --profile +bin/rspec spec -b --format progress --profile echo echo "--------------------------------------------------------------------" @@ -29,13 +24,55 @@ if is_jruby; then echo "Skipping one-by-one spec runs due to expensive JVM load time" else for file in `find spec -iname '*_spec.rb'`; do - NO_COVERAGE=1 bin/rspec $file -b --format progress + bin/rspec $file -b --format progress done fi -# TODO: it would be nice to figure out how to run the cukes w/o the overhead of -# bundler, but just running `bin/cucumber` can fail due to the fact that it -# shells out (via aruba) and executes `rspec`--which can pick up the wrong -# rspec version if we're not running with bundler. -bundle exec cucumber --strict +# force jRuby to use client mode JVM or a compilation mode thats as close as possible, +# idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time +export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' + +# Prepare RUBYOPT for scenarios that are shelling out to ruby, +# and PATH for those that are using `rspec` or `rake`. +# RUBYOPT="-I${PWD}/bundle -rbundler/setup" \ +# PATH="${PWD}/bin:$PATH" \ +# bin/cucumber + +# For now, use this instead, due to a bug in bundler: +# https://github.com/carlhuda/bundler/issues/2382 +bundle exec bin/cucumber --strict + +# Test against other RSpec gems. +unset BUNDLE_GEMFILE + +# Delete the directory for idempotency when running locally +export TMPDIR="/tmp" +rm -rf $TMPDIR/rspec-ci +mkdir $TMPDIR/rspec-ci + +# Get the branch to test against +MAINTENENCE_BRANCH=`cat maintenence-branch` + +cp -r "./" "$TMPDIR/rspec-ci/rspec-mocks" + +#Change to the CI directory +cd $TMPDIR/rspec-ci + +# The steps to test the gems are the same, this function does them. +function test_gem { + git clone git://github.com/rspec/rspec-$1 --depth 1 + cd rspec-$1 + pwd + git fetch origin + git checkout $MAINTENENCE_BRANCH + git branch + bundle_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + + bundle install $bundle_flags + bin/rspec -b + cd ../ +} +# Test rspec-core and rspec-expectations. +test_gem "core" +test_gem "expectations" From ef7fcbc6f739f53cf468ba4f3cf08b8ceabb669f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 22 Jan 2014 23:13:44 -0800 Subject: [PATCH 0430/1179] Update .travis.yml. - Build against ruby-head and jruby-head but allow failures. - We don't need the before_install stuff anymore. --- .travis.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index e8cbfd8bc..a1e515e2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,13 +6,15 @@ rvm: - 1.9.3 - 2.0.0 - 2.1.0 + - ruby-head - ree - jruby-18mode - jruby-19mode + - jruby-head - rbx -before_install: - - gem update bundler - - bundle --version - - gem update --system 2.1.11 - - gem --version +matrix: + allow_failures: + - rvm: jruby-head + - rvm: ruby-head + fast_finish: true From 7f0828a7694445908cf8f97e2fb948dc550d7cd2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 23 Jan 2014 23:27:32 -0800 Subject: [PATCH 0431/1179] Add travis_retry to bundle install. --- script/test_all | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/script/test_all b/script/test_all index ebbb3224e..76f8eaf37 100755 --- a/script/test_all +++ b/script/test_all @@ -58,6 +58,27 @@ cp -r "./" "$TMPDIR/rspec-ci/rspec-mocks" #Change to the CI directory cd $TMPDIR/rspec-ci +travis_retry() { + local result=0 + local count=1 + while [ $count -le 3 ]; do + [ $result -ne 0 ] && { + echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 + } + "$@" + result=$? + [ $result -eq 0 ] && break + count=$(($count + 1)) + sleep 1 + done + + [ $count -eq 3 ] && { + echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 + } + + return $result +} + # The steps to test the gems are the same, this function does them. function test_gem { git clone git://github.com/rspec/rspec-$1 --depth 1 @@ -68,7 +89,7 @@ function test_gem { git branch bundle_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - bundle install $bundle_flags + travis_retry bundle install $bundle_flags bin/rspec -b cd ../ } From d58bab97b7d68b75305a08ea4537faaa2b7528cb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 24 Jan 2014 09:25:52 -0800 Subject: [PATCH 0432/1179] Revamp travis build. - Only run the one-by-one specs and the builds for other gems on MRI. JRuby and RBX are too slow and flakey. - Before `bundle install`, clone all repos to sibling dirs. This makes the later bundle installs for the other repos *much* faster since they don't have to re-clone the rspec repos and can instead share the same checkouts. - Using a common bundle directory to minimize the number of gems that have to be re-installed. - Reinstate using bin/cucumber for faster cuke runs. Running w/o bundler removes the "bundler tax" of having bundle to runtime checks everytime we shell out to `rspec`. - Reorganize build scripts for easier maintenance. - Make certain parts of the build idempotent, so that, for example, the main repo isn't re-cloned or the specs re-run. This will allow us to re-use these scripts in all repos w/o changing them. Otherwise each would have to be a bit different and skip the extra clone and run of their own repo and specs. --- .travis.yml | 5 +- script/clone_all_rspec_repos | 18 +++++++ script/functions.sh | 82 +++++++++++++++++++++++++++++ script/run_build | 18 +++++++ script/test_all | 99 ------------------------------------ 5 files changed, 121 insertions(+), 101 deletions(-) create mode 100755 script/clone_all_rspec_repos create mode 100644 script/functions.sh create mode 100755 script/run_build delete mode 100755 script/test_all diff --git a/.travis.yml b/.travis.yml index a1e515e2e..cf5f2afe9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ -bundler_args: "--binstubs --standalone --without documentation" -script: "script/test_all" +before_install: "script/clone_all_rspec_repos" +bundler_args: "--binstubs --standalone --without documentation --path ../bundle" +script: "script/run_build" rvm: - 1.8.7 - 1.9.2 diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos new file mode 100755 index 000000000..9d2b7d2ee --- /dev/null +++ b/script/clone_all_rspec_repos @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e -x +source script/functions.sh + +if is_mri; then + pushd .. + + clone_repo "rspec" + clone_repo "rspec-core" + clone_repo "rspec-expectations" + clone_repo "rspec-mocks" + clone_repo "rspec-support" + + popd +else + echo "Not cloning all repos since we are not on MRI and they are only needed for the MRI build" +fi diff --git a/script/functions.sh b/script/functions.sh new file mode 100644 index 000000000..1b7aee4df --- /dev/null +++ b/script/functions.sh @@ -0,0 +1,82 @@ +# idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html +export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived +SPECS_HAVE_RUN_FILE=specs.out +MAINTENENCE_BRANCH=`cat maintenence-branch` +BUNDLE_INSTALL_FLAGS=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + +# Taken from: +# https://github.com/travis-ci/travis-build/blob/e9314616e182a23e6a280199cd9070bfc7cae548/lib/travis/build/script/templates/header.sh#L34-L53 +travis_retry() { + local result=0 + local count=1 + while [ $count -le 3 ]; do + [ $result -ne 0 ] && { + echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 + } + "$@" + result=$? + [ $result -eq 0 ] && break + count=$(($count + 1)) + sleep 1 + done + + [ $count -eq 3 ] && { + echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 + } + + return $result +} + +function is_mri() { + if ruby -e "exit(!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby')"; then + # RUBY_ENGINE only returns 'ruby' on MRI. + # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) + return 0 + else + return 1 + fi; +} + +function clone_repo { + if [ ! -d $1 ]; then # don't clone if the dir is already there + travis_retry git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENENCE_BRANCH + fi; +} + +function run_specs_and_record_done { + bin/rspec spec --backtrace --format progress --profile --format progress --out $SPECS_HAVE_RUN_FILE +} + +function run_cukes { + # force jRuby to use client mode JVM or a compilation mode thats as close as possible, + # idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time + # Note that we delay setting this until we run the cukes because we've seen + # spec failures in our spec suite due to problems with this mode. + export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' + + # Prepare RUBYOPT for scenarios that are shelling out to ruby, + # and PATH for those that are using `rspec` or `rake`. + RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ + PATH="${PWD}/bin:$PATH" \ + bin/cucumber --strict +} + +function run_specs_one_by_one { + for file in `find spec -iname '*_spec.rb'`; do + bin/rspec $file -b --format progress + done +} + +function run_spec_suite_for { + if [ ! -f ../$1/$SPECS_HAVE_RUN_FILE ]; then # don't rerun specs that have already run + pushd ../$1 + echo + echo "Running specs for $1" + echo + unset BUNDLE_GEMFILE + travis_retry bundle install $BUNDLE_INSTALL_FLAGS + run_specs_and_record_done + popd + fi; +} + diff --git a/script/run_build b/script/run_build new file mode 100755 index 000000000..ffbedcd6a --- /dev/null +++ b/script/run_build @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e -x +source script/functions.sh + +run_specs_and_record_done +run_cukes + +if is_mri; then + run_specs_one_by_one + run_spec_suite_for "rspec-core" + run_spec_suite_for "rspec-expectations" + run_spec_suite_for "rspec-mocks" + run_spec_suite_for "rspec-support" +else + echo "Skipping the rest of the build on non-MRI rubies" +fi + diff --git a/script/test_all b/script/test_all deleted file mode 100755 index 76f8eaf37..000000000 --- a/script/test_all +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash - -set -e -x - -function is_jruby() { - if ruby -e 'exit RUBY_PLATFORM == "java"'; then - return 0 - else - return 1 - fi -} - -# idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html -export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived - -echo "Running all..." -bin/rspec spec -b --format progress --profile - -echo -echo "--------------------------------------------------------------------" -echo - -if is_jruby; then - echo "Skipping one-by-one spec runs due to expensive JVM load time" -else - for file in `find spec -iname '*_spec.rb'`; do - bin/rspec $file -b --format progress - done -fi - -# force jRuby to use client mode JVM or a compilation mode thats as close as possible, -# idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time -export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' - -# Prepare RUBYOPT for scenarios that are shelling out to ruby, -# and PATH for those that are using `rspec` or `rake`. -# RUBYOPT="-I${PWD}/bundle -rbundler/setup" \ -# PATH="${PWD}/bin:$PATH" \ -# bin/cucumber - -# For now, use this instead, due to a bug in bundler: -# https://github.com/carlhuda/bundler/issues/2382 -bundle exec bin/cucumber --strict - -# Test against other RSpec gems. -unset BUNDLE_GEMFILE - -# Delete the directory for idempotency when running locally -export TMPDIR="/tmp" -rm -rf $TMPDIR/rspec-ci -mkdir $TMPDIR/rspec-ci - -# Get the branch to test against -MAINTENENCE_BRANCH=`cat maintenence-branch` - -cp -r "./" "$TMPDIR/rspec-ci/rspec-mocks" - -#Change to the CI directory -cd $TMPDIR/rspec-ci - -travis_retry() { - local result=0 - local count=1 - while [ $count -le 3 ]; do - [ $result -ne 0 ] && { - echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 - } - "$@" - result=$? - [ $result -eq 0 ] && break - count=$(($count + 1)) - sleep 1 - done - - [ $count -eq 3 ] && { - echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 - } - - return $result -} - -# The steps to test the gems are the same, this function does them. -function test_gem { - git clone git://github.com/rspec/rspec-$1 --depth 1 - cd rspec-$1 - pwd - git fetch origin - git checkout $MAINTENENCE_BRANCH - git branch - bundle_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - - travis_retry bundle install $bundle_flags - bin/rspec -b - cd ../ -} - -# Test rspec-core and rspec-expectations. -test_gem "core" -test_gem "expectations" From a828f11bc5897e57bf2722505e51c6e28333802d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 24 Jan 2014 10:42:57 -0800 Subject: [PATCH 0433/1179] We're still having cucumber problems with bin/cucumber :(. --- script/functions.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/script/functions.sh b/script/functions.sh index 1b7aee4df..0fba5337a 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -50,15 +50,20 @@ function run_specs_and_record_done { function run_cukes { # force jRuby to use client mode JVM or a compilation mode thats as close as possible, # idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time + # # Note that we delay setting this until we run the cukes because we've seen # spec failures in our spec suite due to problems with this mode. export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' # Prepare RUBYOPT for scenarios that are shelling out to ruby, # and PATH for those that are using `rspec` or `rake`. - RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ - PATH="${PWD}/bin:$PATH" \ - bin/cucumber --strict + #RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ + #PATH="${PWD}/bin:$PATH" \ + #bin/cucumber --strict + + # We would like to use the above command but it causes SystemStackError + # on some rubies and we're not yet sure why. + bundle exec cucumber --strict } function run_specs_one_by_one { From 34be2591d2bc3de52358cac04db988de73edf708 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 24 Jan 2014 11:02:05 -0800 Subject: [PATCH 0434/1179] Use `bundle exec cucumber` only on 1.9.2. It's the only ruby that gets a system stack error from the `bin/cucumber` approach. `bin/cucumber` is faster as it avoids loading bundler at runtime. --- script/functions.sh | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/script/functions.sh b/script/functions.sh index 0fba5337a..0e163aae3 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -27,7 +27,7 @@ travis_retry() { return $result } -function is_mri() { +function is_mri { if ruby -e "exit(!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby')"; then # RUBY_ENGINE only returns 'ruby' on MRI. # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) @@ -37,6 +37,18 @@ function is_mri() { fi; } +function is_mri_192 { + if is_mri; then + if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there travis_retry git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENENCE_BRANCH @@ -55,15 +67,18 @@ function run_cukes { # spec failures in our spec suite due to problems with this mode. export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' - # Prepare RUBYOPT for scenarios that are shelling out to ruby, - # and PATH for those that are using `rspec` or `rake`. - #RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ - #PATH="${PWD}/bin:$PATH" \ - #bin/cucumber --strict - - # We would like to use the above command but it causes SystemStackError - # on some rubies and we're not yet sure why. - bundle exec cucumber --strict + if is_mri_192; then + # For some reason we get SystemStackError on 1.9.2 when using + # the bin/cucumber approach below. That approach is faster + # (as it avoids the bundler tax), so we use it on rubies where we can. + bundle exec cucumber --strict + else + # Prepare RUBYOPT for scenarios that are shelling out to ruby, + # and PATH for those that are using `rspec` or `rake`. + RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ + PATH="${PWD}/bin:$PATH" \ + bin/cucumber --strict + fi } function run_specs_one_by_one { From 9ad40d12532fa036bc70729790435c86ce3ae624 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 24 Jan 2014 19:39:30 -0800 Subject: [PATCH 0435/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 3 +++ script/clone_all_rspec_repos | 2 ++ script/functions.sh | 39 ++++++++++++++++++++---------------- script/run_build | 2 ++ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index cf5f2afe9..773ccb88c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +# This file was generated on 2014-01-24T19:39:30-08:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + before_install: "script/clone_all_rspec_repos" bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 9d2b7d2ee..062250f26 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,4 +1,6 @@ #!/bin/bash +# This file was generated on 2014-01-24T19:39:30-08:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x source script/functions.sh diff --git a/script/functions.sh b/script/functions.sh index 0e163aae3..9d035bb4b 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,3 +1,6 @@ +# This file was generated on 2014-01-24T19:39:30-08:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived SPECS_HAVE_RUN_FILE=specs.out @@ -60,24 +63,26 @@ function run_specs_and_record_done { } function run_cukes { - # force jRuby to use client mode JVM or a compilation mode thats as close as possible, - # idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time - # - # Note that we delay setting this until we run the cukes because we've seen - # spec failures in our spec suite due to problems with this mode. - export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' + if [ -d features ]; then + # force jRuby to use client mode JVM or a compilation mode thats as close as possible, + # idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time + # + # Note that we delay setting this until we run the cukes because we've seen + # spec failures in our spec suite due to problems with this mode. + export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' - if is_mri_192; then - # For some reason we get SystemStackError on 1.9.2 when using - # the bin/cucumber approach below. That approach is faster - # (as it avoids the bundler tax), so we use it on rubies where we can. - bundle exec cucumber --strict - else - # Prepare RUBYOPT for scenarios that are shelling out to ruby, - # and PATH for those that are using `rspec` or `rake`. - RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ - PATH="${PWD}/bin:$PATH" \ - bin/cucumber --strict + if is_mri_192; then + # For some reason we get SystemStackError on 1.9.2 when using + # the bin/cucumber approach below. That approach is faster + # (as it avoids the bundler tax), so we use it on rubies where we can. + bundle exec cucumber --strict + else + # Prepare RUBYOPT for scenarios that are shelling out to ruby, + # and PATH for those that are using `rspec` or `rake`. + RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ + PATH="${PWD}/bin:$PATH" \ + bin/cucumber --strict + fi fi } diff --git a/script/run_build b/script/run_build index ffbedcd6a..35bbf5f07 100755 --- a/script/run_build +++ b/script/run_build @@ -1,4 +1,6 @@ #!/bin/bash +# This file was generated on 2014-01-24T19:39:30-08:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x source script/functions.sh From 6464e4cc851ca6ee86469a5af5bbc372c99692da Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Fri, 24 Jan 2014 20:30:00 -0800 Subject: [PATCH 0436/1179] Better error message when calling private method on a verifying double. Fixes #531. --- lib/rspec/mocks/verifying_double.rb | 7 +++++-- spec/rspec/mocks/verifying_double_spec.rb | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 9baeac1d5..97c49c712 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -65,7 +65,10 @@ class InstanceVerifyingDouble def initialize(doubled_module, *args) @doubled_module = doubled_module - __initialize_as_test_double(doubled_module, *args) + __initialize_as_test_double( + "#{doubled_module.description} (instance)", + *args + ) end def __build_mock_proxy(order_group) @@ -86,7 +89,7 @@ module ObjectVerifyingDoubleMethods def initialize(doubled_module, *args) @doubled_module = doubled_module - __initialize_as_test_double(doubled_module, *args) + __initialize_as_test_double(doubled_module.description, *args) end def __build_mock_proxy(order_group) diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 9b9937b90..d100f8964 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -131,6 +131,14 @@ module Mocks expect(o.send(:msg)).to eq("received") end + it 'gives a descriptive error message for NoMethodError' do + o = instance_double("LoadedClass") + expect { + o.defined_private_method + }.to raise_error(NoMethodError, + /Double "LoadedClass \(instance\)"/) + end + describe "method visibility" do shared_examples_for "preserves method visibility" do |visibility| method_name = :"defined_#{visibility}_method" @@ -303,6 +311,13 @@ module Mocks prevents { o.should_receive(:defined_instance_method) } end + it 'gives a descriptive error message for NoMethodError' do + o = class_double("LoadedClass") + expect { + o.defined_private_class_method + }.to raise_error(NoMethodError, /Double "LoadedClass"/) + end + describe "method visibility" do shared_examples_for "preserves method visibility" do |visibility| method_name = :"defined_#{visibility}_class_method" From 77e9bc54ecbed33099b4c615b0c3dce2b9f13e90 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 25 Jan 2014 09:32:04 -0800 Subject: [PATCH 0437/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 3 +-- maintenance-branch | 1 + maintenence-branch | 1 - script/clone_all_rspec_repos | 2 +- script/functions.sh | 17 ++++++++++++----- script/run_build | 3 +-- 6 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 maintenance-branch delete mode 100644 maintenence-branch diff --git a/.travis.yml b/.travis.yml index 773ccb88c..b5970932f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-01-24T19:39:30-08:00 from the rspec-dev repo. +# This file was generated on 2014-01-25T09:32:04-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: "script/clone_all_rspec_repos" @@ -21,4 +21,3 @@ matrix: - rvm: jruby-head - rvm: ruby-head fast_finish: true - diff --git a/maintenance-branch b/maintenance-branch new file mode 100644 index 000000000..8b25206ff --- /dev/null +++ b/maintenance-branch @@ -0,0 +1 @@ +master \ No newline at end of file diff --git a/maintenence-branch b/maintenence-branch deleted file mode 100644 index 1f7391f92..000000000 --- a/maintenence-branch +++ /dev/null @@ -1 +0,0 @@ -master diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 062250f26..12ecda828 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-01-24T19:39:30-08:00 from the rspec-dev repo. +# This file was generated on 2014-01-25T09:32:04-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 9d035bb4b..3855d648c 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,10 +1,10 @@ -# This file was generated on 2014-01-24T19:39:30-08:00 from the rspec-dev repo. +# This file was generated on 2014-01-25T09:32:04-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived SPECS_HAVE_RUN_FILE=specs.out -MAINTENENCE_BRANCH=`cat maintenence-branch` +MAINTENANCE_BRANCH=`cat maintenance-branch` BUNDLE_INSTALL_FLAGS=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` # Taken from: @@ -54,12 +54,20 @@ function is_mri_192 { function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there - travis_retry git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENENCE_BRANCH + travis_retry git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH fi; } function run_specs_and_record_done { - bin/rspec spec --backtrace --format progress --profile --format progress --out $SPECS_HAVE_RUN_FILE + local rspec_bin=bin/rspec + + # rspec-core needs to run with a special script that loads simplecov first, + # so that it can instrument rspec-core's code before rspec-core has been loaded. + if [ -f script/rspec_with_simplecov ]; then + rspec_bin=script/rspec_with_simplecov + fi; + + $rspec_bin spec --backtrace --format progress --profile --format progress --out $SPECS_HAVE_RUN_FILE } function run_cukes { @@ -104,4 +112,3 @@ function run_spec_suite_for { popd fi; } - diff --git a/script/run_build b/script/run_build index 35bbf5f07..3fbf66dff 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-01-24T19:39:30-08:00 from the rspec-dev repo. +# This file was generated on 2014-01-25T09:32:04-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -17,4 +17,3 @@ if is_mri; then else echo "Skipping the rest of the build on non-MRI rubies" fi - From d309175d83bfd190826515bb72d8766997910c9f Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 25 Jan 2014 14:13:08 -0800 Subject: [PATCH 0438/1179] Justify uses of known techniques that bust the method cache. I don't see a way to remove any of these. Addresses #268. --- script/ignores | 53 +++++++++++++++++++++++++++++ script/list_method_cache_busters.sh | 23 +++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 script/ignores create mode 100755 script/list_method_cache_busters.sh diff --git a/script/ignores b/script/ignores new file mode 100644 index 000000000..7ea1429c3 --- /dev/null +++ b/script/ignores @@ -0,0 +1,53 @@ +# grep -v -f doesn't work properly when empty, so this line is here. + +# The `alias_method` calls below are only executed at file load time +# (when the method cache will be busted by defining methods anyway). +lib/rspec/mocks/extensions/marshal.rb: undef_method :dump +lib/rspec/mocks/argument_matchers.rb: alias_method :hash_not_including, :hash_excluding +lib/rspec/mocks/argument_matchers.rb: alias_method :an_instance_of, :instance_of +lib/rspec/mocks/argument_matchers.rb: alias_method :a_kind_of, :kind_of +lib/rspec/mocks/extensions/marshal.rb: alias_method :dump_without_mocks, :dump +lib/rspec/mocks/extensions/marshal.rb: alias_method :dump, :dump_with_mocks +lib/rspec/mocks/method_double.rb: alias_method :save_original_method!, :original_method +lib/rspec/mocks/test_double.rb: alias_method :to_str, :to_s +lib/rspec/mocks/error_generator.rb: MockExpectationError = Class.new(Exception) +lib/rspec/mocks/error_generator.rb: ExpiredTestDoubleError = Class.new(MockExpectationError) +lib/rspec/mocks/error_generator.rb: OutsideOfExampleError = Class.new(StandardError) +lib/rspec/mocks/error_generator.rb: UnsupportedMatcherError = Class.new(StandardError) +lib/rspec/mocks/error_generator.rb: NegationUnsupportedError = Class.new(StandardError) +lib/rspec/mocks/message_expectation.rb: CannotModifyFurtherError = Class.new(StandardError) +lib/rspec/mocks/mutate_const.rb: extend RecursiveConstMethods +lib/rspec/mocks/mutate_const.rb: extend RecursiveConstMethods + +# False positives due to naming +lib/rspec/mocks/any_instance/recorder.rb: def build_alias_method_name(method_name) +lib/rspec/mocks/any_instance/recorder.rb: if public_protected_or_private_method_defined?(build_alias_method_name(method_name)) +lib/rspec/mocks/any_instance/recorder.rb: alias_method_name = build_alias_method_name(method_name) +lib/rspec/mocks/any_instance/recorder.rb: alias_method_name = build_alias_method_name(method_name) +lib/rspec/mocks/proxy.rb: build_alias_method_name(message) + +# Instance method stashing needs to blow away method cache, no way around it. +lib/rspec/mocks/any_instance/recorder.rb: alias_method method_name, alias_method_name +lib/rspec/mocks/any_instance/recorder.rb: alias_method alias_method_name, method_name +lib/rspec/mocks/any_instance/recorder.rb: remove_method method_name +lib/rspec/mocks/any_instance/recorder.rb: remove_method alias_method_name +lib/rspec/mocks/any_instance/recorder.rb: remove_method method_name +lib/rspec/mocks/instance_method_stasher.rb: @klass.__send__(:alias_method, stashed_method_name, @method) +lib/rspec/mocks/instance_method_stasher.rb: @klass.__send__(:alias_method, @method, stashed_method_name) +lib/rspec/mocks/instance_method_stasher.rb: @klass.__send__(:remove_method, stashed_method_name) + +# Constant stubbing needs to blow away method cache, no way around it. +lib/rspec/mocks/method_double.rb: object_singleton_class.__send__(:remove_method, @method_name) +lib/rspec/mocks/mutate_const.rb: @context.const_set(@const_name, @original_value) +lib/rspec/mocks/mutate_const.rb: @context.const_set(@const_name, @mutated_value) +lib/rspec/mocks/mutate_const.rb: @context.const_set(@const_name, @original_value) +lib/rspec/mocks/mutate_const.rb: @mutated_value.const_set(const, get_const_defined_on(original_value, const)) +lib/rspec/mocks/mutate_const.rb: klass.const_set(name, Module.new) +lib/rspec/mocks/mutate_const.rb: context.const_set(@const_name, @mutated_value) +lib/rspec/mocks/mutate_const.rb: @context.__send__(:remove_const, @const_name) +lib/rspec/mocks/mutate_const.rb: @context.__send__(:remove_const, @const_name) +lib/rspec/mocks/mutate_const.rb: @context.__send__(:remove_const, @const_name) +lib/rspec/mocks/mutate_const.rb: @deepest_defined_const.__send__(:remove_const, @const_to_remove) + +# We provide our own wrapper around extend for others to use if they choose. +lib/rspec/mocks/test_double.rb: object.extend self diff --git a/script/list_method_cache_busters.sh b/script/list_method_cache_busters.sh new file mode 100755 index 000000000..6efbdc4ab --- /dev/null +++ b/script/list_method_cache_busters.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# set -x + +# This list is from https://charlie.bz/blog/things-that-clear-rubys-method-cache + +IGNORE_FILE=/tmp/cache_busters_ignore +COMMENT_LINE_RE="^(\w|\/)+\.rb: +#" + +cat script/ignores | grep -v "^$" | ruby -ne 'puts $_.split(/\s+###/)[0]' > $IGNORE_FILE + +egrep 'def [a-z]*\..*' -R lib | grep -v "def self" | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +egrep 'undef\\b' -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep alias_method -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep remove_method -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep const_set -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep remove_const -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +egrep '\bextend\b' -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep 'Class.new' -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep private_constant -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep public_constant -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep "Marshal.load" -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" +grep "OpenStruct.new" -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" + From 1827ee45b29346f7be078f5e5a0e10ad2f8e5b61 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Fri, 24 Jan 2014 20:48:07 -0800 Subject: [PATCH 0439/1179] Correct stub undefined parent modules all the way down when stubbing a nested constant. This is necessary for verifying doubles to work correctly, since they need to distinguish between real and stubbed constants. Fixes #529. --- Changelog.md | 2 ++ lib/rspec/mocks/mutate_const.rb | 31 ++++++++++++++--------- spec/rspec/mocks/mutate_const_spec.rb | 12 +++++++++ spec/rspec/mocks/verifying_double_spec.rb | 7 +++++ 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4eda0b457..e057ce758 100644 --- a/Changelog.md +++ b/Changelog.md @@ -43,6 +43,8 @@ Bug Fixes: * Fix regression in `stub_chain`/`receive_message_chain` that caused it to raise an `ArgumentError` when passing args to the stubbed methods. (Sam Phippen) +* Correct stub of undefined parent modules all the way down when stubbing a + nested constant. (Xavier Shay) Enhancements: diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index ce12007f5..377b270ac 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -322,19 +322,15 @@ def verify_constants_to_transfer! # @api private class UndefinedConstantSetter < BaseMutator def mutate - remaining_parts = @context_parts.dup - @deepest_defined_const = @context_parts.inject(Object) do |klass, name| - break klass unless const_defined_on?(klass, name) - remaining_parts.shift - get_const_defined_on(klass, name) - end - - context = remaining_parts.inject(@deepest_defined_const) do |klass, name| - klass.const_set(name, Module.new) + @parent = @context_parts.inject(Object) do |klass, name| + if const_defined_on?(klass, name) + get_const_defined_on(klass, name) + else + ConstantMutator.stub(name_for(klass, name), Module.new) + end end - @const_to_remove = remaining_parts.first || @const_name - context.const_set(@const_name, @mutated_value) + @parent.const_set(@const_name, @mutated_value) end def to_constant @@ -346,7 +342,18 @@ def to_constant end def reset - @deepest_defined_const.__send__(:remove_const, @const_to_remove) + @parent.__send__(:remove_const, @const_name) + end + + private + + def name_for(parent, name) + root = if parent == Object + '' + else + parent.name + end + root + '::' + name end end diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 197650f76..0f95620ee 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -451,6 +451,18 @@ def change_const_value_to(value) it("returns nil for the original value") { expect(const.original_value).to be_nil } end + context 'for a previously undefined parent of a stubbed constant' do + before { stub_const("TestClass::UndefinedModule::Undefined", :other) } + let(:const) { Constant.original("TestClass::UndefinedModule") } + + it("exposes its name") { expect(const.name).to eq("TestClass::UndefinedModule") } + it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } + it("indicates it has been mutated") { expect(const).to be_mutated } + it("indicates it has been stubbed") { expect(const).to be_stubbed } + it("indicates it has not been hidden") { expect(const).not_to be_hidden } + it("returns nil for the original value") { expect(const.original_value).to be_nil } + end + context 'for a previously undefined unstubbed constant' do let(:const) { Constant.original("TestClass::Undefined") } diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index d100f8964..0c9cba336 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -84,6 +84,13 @@ module Mocks expect(o.undefined_instance_method(:arg)).to eq(true) end + it 'handles classes that are materialized after mocking' do + stub_const "A::B", Object.new + o = instance_double "A", :undefined_instance_method => true + + expect(o.undefined_instance_method).to eq(true) + end + context 'for null objects' do let(:o) { instance_double('NonLoadedClass').as_null_object } From 0c06dce2d0759c3d6892ba98e500e70aefac9ffe Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 27 Jan 2014 15:48:20 +1100 Subject: [PATCH 0440/1179] cleanup stray whitespace --- Rakefile | 1 - benchmarks/find_original_method_early.rb | 1 - features/message_expectations/call_original.feature | 1 - features/mutating_constants/stub_defined_constant.feature | 1 - features/verifying_doubles/dynamic_classes.feature | 1 - lib/rspec/mocks.rb | 1 - lib/rspec/mocks/any_instance/expectation_chain.rb | 1 - lib/rspec/mocks/arity_calculator.rb | 1 - lib/rspec/mocks/instance_method_stasher.rb | 1 - lib/rspec/mocks/matchers/have_received.rb | 1 - lib/rspec/mocks/matchers/receive.rb | 1 - lib/rspec/mocks/object_reference.rb | 1 - lib/rspec/mocks/syntax.rb | 1 - lib/rspec/mocks/verifying_message_expecation.rb | 1 - script/list_method_cache_busters.sh | 1 - spec/rspec/mocks/and_call_original_spec.rb | 1 - spec/rspec/mocks/and_yield_spec.rb | 1 - spec/rspec/mocks/combining_implementation_instructions_spec.rb | 1 - spec/rspec/mocks/configuration_spec.rb | 1 - spec/rspec/mocks/double_spec.rb | 1 - spec/rspec/mocks/example_methods_spec.rb | 1 - spec/rspec/mocks/instance_method_stasher_spec.rb | 1 - spec/rspec/mocks/matchers/receive_spec.rb | 1 - spec/rspec/mocks/methods_spec.rb | 1 - spec/rspec/mocks/mutate_const_spec.rb | 1 - spec/rspec/mocks/nil_expectation_warning_spec.rb | 1 - spec/rspec/mocks/test_double_spec.rb | 1 - spec/rspec/mocks_spec.rb | 1 - 28 files changed, 28 deletions(-) diff --git a/Rakefile b/Rakefile index 6f301c88c..d88cc6cdd 100644 --- a/Rakefile +++ b/Rakefile @@ -53,4 +53,3 @@ task :verify_private_key_present do end task :build => :verify_private_key_present - diff --git a/benchmarks/find_original_method_early.rb b/benchmarks/find_original_method_early.rb index 596c6c218..875ea9d02 100644 --- a/benchmarks/find_original_method_early.rb +++ b/benchmarks/find_original_method_early.rb @@ -62,4 +62,3 @@ Find original - test double 0.980000 0.010000 0.990000 ( 0.983577) =end - diff --git a/features/message_expectations/call_original.feature b/features/message_expectations/call_original.feature index 8a9a7cf30..7c26d3d76 100644 --- a/features/message_expectations/call_original.feature +++ b/features/message_expectations/call_original.feature @@ -21,4 +21,3 @@ Feature: Calling the original method """ When I run `rspec call_original_spec.rb` Then the examples should all pass - diff --git a/features/mutating_constants/stub_defined_constant.feature b/features/mutating_constants/stub_defined_constant.feature index 935daccb7..ddd0238db 100644 --- a/features/mutating_constants/stub_defined_constant.feature +++ b/features/mutating_constants/stub_defined_constant.feature @@ -76,4 +76,3 @@ Feature: Stub Defined Constant """ When I run `rspec stub_const_spec.rb` Then the examples should all pass - diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index 8d297fc88..d6f9850dc 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -69,4 +69,3 @@ Feature: Dynamic classes When I run `rspec spec/user_spec.rb` Then the examples should all pass - diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 99aa995a9..16c56743d 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -85,4 +85,3 @@ class << self; attr_reader :space; end IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored' end end - diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 073cf096f..82c3015d7 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -44,4 +44,3 @@ def invocation_order end end end - diff --git a/lib/rspec/mocks/arity_calculator.rb b/lib/rspec/mocks/arity_calculator.rb index 5f519a910..a9c499501 100644 --- a/lib/rspec/mocks/arity_calculator.rb +++ b/lib/rspec/mocks/arity_calculator.rb @@ -63,4 +63,3 @@ def max_arity end end end - diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index fc4d6f742..f3f59969f 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -109,4 +109,3 @@ def method_owned_by_klass? end end end - diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index c0c5ae755..245d08917 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -99,4 +99,3 @@ def mock_proxy end end end - diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 3a4245afc..dee28039a 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -118,4 +118,3 @@ def playback_onto(expectation) end end end - diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index f1162f8db..f99f5f7e7 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -87,4 +87,3 @@ def object end end end - diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 7a20872b8..289911122 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -407,4 +407,3 @@ def self.default_should_syntax_host end end end - diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 7ca8ea2c3..be908faaa 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -58,4 +58,3 @@ def ensure_arity!(actual) end end end - diff --git a/script/list_method_cache_busters.sh b/script/list_method_cache_busters.sh index 6efbdc4ab..5eedd949c 100755 --- a/script/list_method_cache_busters.sh +++ b/script/list_method_cache_busters.sh @@ -20,4 +20,3 @@ grep private_constant -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE grep public_constant -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep "Marshal.load" -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep "OpenStruct.new" -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" - diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index d7aa3a085..82c31b62f 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -266,4 +266,3 @@ def request.perform end end end - diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index 3e0a0761f..aa89dedf2 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -124,4 +124,3 @@ end end end - diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index ca2a34f97..e58e60003 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -211,4 +211,3 @@ def verify_combined_implementation end end end - diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index dec4a07ef..9ffd0c1c5 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -240,4 +240,3 @@ def configure_default_syntax end end end - diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 28b58e192..8b62a6913 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -866,4 +866,3 @@ def add_call end end end - diff --git a/spec/rspec/mocks/example_methods_spec.rb b/spec/rspec/mocks/example_methods_spec.rb index da77ecdd3..ec489e5cc 100644 --- a/spec/rspec/mocks/example_methods_spec.rb +++ b/spec/rspec/mocks/example_methods_spec.rb @@ -11,4 +11,3 @@ module Mocks end end end - diff --git a/spec/rspec/mocks/instance_method_stasher_spec.rb b/spec/rspec/mocks/instance_method_stasher_spec.rb index 1df9e5d79..c76fedfc7 100644 --- a/spec/rspec/mocks/instance_method_stasher_spec.rb +++ b/spec/rspec/mocks/instance_method_stasher_spec.rb @@ -72,4 +72,3 @@ def obj.hello; :hello_defined_on_singleton_class; end; end end end - diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 4c7bb5a73..3b28a2601 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -416,4 +416,3 @@ def eq(value) end end end - diff --git a/spec/rspec/mocks/methods_spec.rb b/spec/rspec/mocks/methods_spec.rb index 9c80b2ff4..74f018844 100644 --- a/spec/rspec/mocks/methods_spec.rb +++ b/spec/rspec/mocks/methods_spec.rb @@ -24,4 +24,3 @@ def added_methods end end end - diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 0f95620ee..4965d70a7 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -540,4 +540,3 @@ def change_const_value_to(value) end end end - diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index 9e020185e..875bd03e8 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -50,4 +50,3 @@ module Mocks end end end - diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 45e1c75e6..4ff8dafbf 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -47,4 +47,3 @@ def use; end end end end - diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 44bb81fc6..69a882d20 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -190,4 +190,3 @@ def capture_error end end end - From eb1a63a6a02df21c354a84b80784c021c6272431 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 25 Jan 2014 16:19:15 -0800 Subject: [PATCH 0441/1179] Support keyword arguments when checking for valid arity. This changes the error message that was included in the ArgumentError raised when arity was incorrect. It used to match the default Ruby message, it now uses our enhanced error message. While it would be technically more correct to emulate the Ruby one, ours is more useful and the logic required would now have to be more complicated to support keyword arguments. Fixes #431. --- Changelog.md | 2 + lib/rspec/mocks/arity_calculator.rb | 77 ++++++++++++++++--- lib/rspec/mocks/error_generator.rb | 7 +- .../mocks/verifying_message_expecation.rb | 14 ++-- lib/rspec/mocks/verifying_proxy.rb | 8 +- spec/rspec/mocks/arity_calculator_spec.rb | 72 ++++++++++++++++- spec/rspec/mocks/partial_double_spec.rb | 5 +- spec/rspec/mocks/verifying_double_spec.rb | 23 ++++++ .../verifying_message_expecation_spec.rb | 7 +- spec/spec_helper.rb | 7 ++ 10 files changed, 189 insertions(+), 33 deletions(-) diff --git a/Changelog.md b/Changelog.md index e057ce758..40c6829cc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -57,6 +57,8 @@ Enhancements: * Add `RSpec::Mocks.with_temporary_scope`, which allows you to create temporary rspec-mocks scopes in arbitrary places (such as a `before(:all)` hook). (Myron Marston) +* Support keyword arguments when checking arity with verifying doubles. + (Xavier Shay) ### 3.0.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) diff --git a/lib/rspec/mocks/arity_calculator.rb b/lib/rspec/mocks/arity_calculator.rb index a9c499501..33e33d37f 100644 --- a/lib/rspec/mocks/arity_calculator.rb +++ b/lib/rspec/mocks/arity_calculator.rb @@ -9,18 +9,44 @@ def initialize(method) end # @api private - def within_range?(actual) - min_arity <= actual && actual <= max_arity + def matches?(actual_args) + missing_required_keyword_args(actual_args).empty? && + within_range?(actual_args.length) end # @api private + def error_description(actual_args) + missing = missing_required_keyword_args(actual_args) + if missing.empty? + "Wrong number of arguments. Expected %s, got %s." % [ + range_description, + actual_args.length + ] + else + "Missing required keyword arguments: %s" % [ + missing.join(", ") + ] + end + end + + private + def range_description return min_arity.to_s if min_arity == max_arity return "#{min_arity} or more" if max_arity == INFINITY "#{min_arity} to #{max_arity}" end - private + def within_range?(actual) + min_arity <= actual && actual <= max_arity + end + + def missing_required_keyword_args(actual_args) + keyword_args = actual_args.last + keyword_args = {} unless keyword_args.is_a?(Hash) + + required_keyword_args - keyword_args.keys + end def method @method @@ -31,24 +57,47 @@ def self.supports_optional_and_splat_args? Method.method_defined?(:parameters) end - def min_arity - return method.arity if method.arity >= 0 - # `~` inverts the one's complement and gives us the number of - # required arguments. - ~method.arity - end - if supports_optional_and_splat_args? + def required_keyword_args + method.parameters.map {|type, name| + name if type == :keyreq + }.compact + end + + def min_arity + if method.arity >= 0 + method.arity + else + # `~` inverts the one's complement and gives us the number of + # required arguments. + ~method.arity + end + parameter_modifier(:keyreq) + end + def max_arity params = method.parameters if params.any? {|(type, _)| type == :rest } # Method takes a splat argument return INFINITY else - params.count {|(type, _)| type != :block } + # TODO: Optional keyword args + params.count {|(type, _)| + ![:block, :keyreq, :key].include?(type) + } + parameter_modifier(:key) end end else + def required_keyword_args + [] + end + + def min_arity + return method.arity if method.arity >= 0 + # `~` inverts the one's complement and gives us the number of + # required arguments. + ~method.arity + end + def max_arity # On 1.8, Method#parameters does not exist. There is no way to # distinguish between default and splat args, so there is no way to @@ -59,6 +108,12 @@ def max_arity end end + def parameter_modifier(parameter_type) + mod = method.parameters.any? {|type, _| + type == parameter_type + } ? 1 : 0 + end + INFINITY = 1/0.0 end end diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index b2f03a52a..425267bd7 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -81,11 +81,8 @@ def raise_non_public_error(method_name, visibility) end # @private - def raise_arity_error(calculator, actual) - __raise "Wrong number of arguments. Expected %s, got %s." % [ - calculator.range_description, - actual - ] + def raise_arity_error(calculator, actual_args) + __raise calculator.error_description(actual_args) end # @private diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index be908faaa..8641bfe65 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -26,32 +26,32 @@ def initialize(*args) # @override def with(*args, &block) unless ArgumentMatchers::AnyArgsMatcher === args.first - expected_arity = if ArgumentMatchers::NoArgsMatcher === args.first - 0 + expected_args = if ArgumentMatchers::NoArgsMatcher === args.first + [] elsif args.length > 0 - args.length + args else # No arguments given, this will raise. super end - ensure_arity!(expected_arity) + ensure_arity!(expected_args) end super end private - def ensure_arity!(actual) + def ensure_arity!(actual_args) return if method_reference.nil? method_reference.when_defined do |method| calculator = ArityCalculator.new(method) - unless calculator.within_range?(actual) + unless calculator.matches?(actual_args) # Fail fast is required, otherwise the message expecation will fail # as well ("expected method not called") and clobber this one. @failed_fast = true - @error_generator.raise_arity_error(calculator, actual) + @error_generator.raise_arity_error(calculator, actual_args) end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 882590409..3b86e9233 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -118,17 +118,17 @@ def add_expectation(*arg) end def proxy_method_invoked(obj, *args, &block) - ensure_arity!(args.length) + ensure_arity!(args) super end private - def ensure_arity!(arity) + def ensure_arity!(actual_args) @method_reference.when_defined do |method| calculator = ArityCalculator.new(method) - unless calculator.within_range?(arity) - raise ArgumentError, "wrong number of arguments (#{arity} for #{method.arity})" + unless calculator.matches?(actual_args) + raise ArgumentError, calculator.error_description(actual_args) end end end diff --git a/spec/rspec/mocks/arity_calculator_spec.rb b/spec/rspec/mocks/arity_calculator_spec.rb index 864c142f0..796407e47 100644 --- a/spec/rspec/mocks/arity_calculator_spec.rb +++ b/spec/rspec/mocks/arity_calculator_spec.rb @@ -7,11 +7,15 @@ module Mocks subject { described_class.new(test_method) } def within_range?(arity) - subject.within_range?(arity) + subject.matches?([nil] * arity) + end + + def matches?(args) + subject.matches?(args) end def description - subject.range_description + subject.error_description([])[/Expected (.*),/, 1] end describe 'with a method with arguments' do @@ -75,6 +79,70 @@ def arity_optional(x, y, z = 1); end end end + if keyword_args_supported? + describe 'a method with optional keyword arguments' do + eval <<-RUBY + def arity_kw(x, y:1, z:2); end + RUBY + + let(:test_method) { method(:arity_kw) } + + it 'returns false unless all required keywords args are present' do + expect(within_range?(1)).to eq(true) + expect(within_range?(2)).to eq(true) + expect(within_range?(3)).to eq(false) + end + end + end + + if required_keyword_args_supported? + describe 'a method with required keyword arguments' do + eval <<-RUBY + def arity_required_kw(x, y:, z:, a: 'default'); end + RUBY + + let(:test_method) { method(:arity_required_kw) } + + it 'returns false unless all required keywords args are present' do + expect(matches?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(matches?([nil, {:a => 0, :y => 1}])).to eq(false) + expect(matches?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(false) + expect(matches?([nil, nil])).to eq(false) + end + + it 'is described precisely' do + expect(subject.error_description([nil, {:a => 0}])).to \ + eq("Missing required keyword arguments: y, z") + end + + it 'is described precisely when arity is wrong' do + expect(subject.error_description([{:z => 0, :y => 1}])).to \ + eq("Wrong number of arguments. Expected 2, got 1.") + end + end + + describe 'a method with required keyword arguments and a splat' do + eval <<-RUBY + def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end + RUBY + + let(:test_method) { method(:arity_required_kw_splat) } + + it 'returns false unless all required keywords args are present' do + expect(matches?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(matches?([nil, {:a => 0, :y => 1}])).to eq(false) + expect(matches?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(matches?([nil, nil, nil])).to eq(false) + expect(matches?([])).to eq(false) + end + + it 'is described precisely' do + expect(subject.error_description([nil, {:y => 1}])).to \ + eq("Missing required keyword arguments: z") + end + end + end + describe 'a method with a block' do def arity_block(_, &block); end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 184c687ae..5a54d7dde 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -298,7 +298,10 @@ def method_missing(method_name, *args) allow(object).to receive(:implemented) expect { object.implemented('bogus') - }.to raise_error(ArgumentError, /wrong number of arguments \(1 for 0\)/) + }.to raise_error( + ArgumentError, + a_string_including("Wrong number of arguments. Expected 0, got 1.") + ) end end end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 0c9cba336..c4fd38ddd 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -34,6 +34,14 @@ def defined_private_class_method def defined_instance_method end + if required_keyword_args_supported? + # Need to eval this since it is invalid syntax on earlier rubies. + eval <<-RUBY + def kw_args_method(optional_arg:'hello', required_arg:) + end + RUBY + end + def send(*) end @@ -217,6 +225,21 @@ module Mocks }.to raise_error(ArgumentError) end + if required_keyword_args_supported? + it 'allows keyword arguments' do + o = instance_double('LoadedClass', :kw_args_method => true) + expect(o.kw_args_method(:required_arg => 'something')).to eq(true) + end + + it 'checks that stubbed methods with required keyword args are ' + + 'invoked with the required arguments' do + o = instance_double('LoadedClass', :kw_args_method => true) + expect { + o.kw_args_method(:optional_arg => 'something') + }.to raise_error(ArgumentError) + end + end + it 'allows class to be specified by constant' do o = instance_double(LoadedClass, :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 7321b1f32..4c1745550 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -21,11 +21,12 @@ module Mocks describe 'when arity match fails' do it 'raises error' do + args = ["abc123", "xyz987"] subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_arity_error). - with(instance_of(ArityCalculator), 2) + with(instance_of(ArityCalculator), args) - subject.with("abc123", "xyz987") + subject.with(*args) end end @@ -49,7 +50,7 @@ module Mocks it 'matches arity to 0' do subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_arity_error). - with(instance_of(ArityCalculator), 0) + with(instance_of(ArityCalculator), []) subject.with(no_args) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5c61b2665..2c53c9737 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -115,5 +115,12 @@ def prevents(&block) after(:all) do RSpec::Mocks.configuration.syntax = orig_syntax end +end + +def keyword_args_supported? + RUBY_VERSION >= '2.0.0' && RUBY_ENGINE != 'rbx' +end +def required_keyword_args_supported? + RUBY_VERSION >= '2.1.0' && RUBY_ENGINE != 'rbx' end From 72a8e7cd3d285365061ddb802a0b67ff2025f7af Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 26 Jan 2014 16:54:58 -0800 Subject: [PATCH 0442/1179] Extract RubyFeatures module. --- lib/rspec/mocks/arity_calculator.rb | 24 ++++++++++++----------- spec/rspec/mocks/arity_calculator_spec.rb | 4 ++-- spec/rspec/mocks/verifying_double_spec.rb | 2 ++ spec/spec_helper.rb | 11 +++-------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/rspec/mocks/arity_calculator.rb b/lib/rspec/mocks/arity_calculator.rb index 33e33d37f..6f92aceea 100644 --- a/lib/rspec/mocks/arity_calculator.rb +++ b/lib/rspec/mocks/arity_calculator.rb @@ -1,9 +1,10 @@ +require 'rspec/mocks/ruby_features' + module RSpec module Mocks # Figures out the valid arity range for a method. Surprisingly non-trivial. class ArityCalculator - def initialize(method) @method = method end @@ -41,23 +42,24 @@ def within_range?(actual) min_arity <= actual && actual <= max_arity end - def missing_required_keyword_args(actual_args) - keyword_args = actual_args.last - keyword_args = {} unless keyword_args.is_a?(Hash) + if RubyFeatures.required_keyword_args_supported? + def missing_required_keyword_args(actual_args) + keyword_args = actual_args.last + keyword_args = {} unless keyword_args.is_a?(Hash) - required_keyword_args - keyword_args.keys + required_keyword_args - keyword_args.keys + end + else + def missing_required_keyword_args(_) + [] + end end def method @method end - # @api private - def self.supports_optional_and_splat_args? - Method.method_defined?(:parameters) - end - - if supports_optional_and_splat_args? + if RubyFeatures.optional_and_splat_args_supported? def required_keyword_args method.parameters.map {|type, name| name if type == :keyreq diff --git a/spec/rspec/mocks/arity_calculator_spec.rb b/spec/rspec/mocks/arity_calculator_spec.rb index 796407e47..4c3e6a3fe 100644 --- a/spec/rspec/mocks/arity_calculator_spec.rb +++ b/spec/rspec/mocks/arity_calculator_spec.rb @@ -61,14 +61,14 @@ def arity_optional(x, y, z = 1); end expect(within_range?(2)).to eq(true) expect(within_range?(3)).to eq(true) - if ArityCalculator.supports_optional_and_splat_args? + if optional_and_splat_args_supported? expect(within_range?(4)).to eq(false) else expect(within_range?(4)).to eq(true) end end - if ArityCalculator.supports_optional_and_splat_args? + if optional_and_splat_args_supported? it 'is described as a range' do expect(description).to eq("2 to 3") end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index c4fd38ddd..6fd7c480c 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' class LoadedClass + extend RSpec::Mocks::RubyFeatures + M = :m N = :n INSTANCE = LoadedClass.new diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2c53c9737..12e3119c4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ require 'rspec/support/spec' +require 'rspec/mocks/ruby_features' RSpec::Support::Spec.setup_simplecov do minimum_coverage 97 @@ -76,6 +77,8 @@ def prevents(&block) config.include VerifyAndResetHelpers config.include VerificationHelpers + config.extend RSpec::Mocks::RubyFeatures + config.include RSpec::Mocks::RubyFeatures end shared_context "with syntax" do |syntax| @@ -116,11 +119,3 @@ def prevents(&block) RSpec::Mocks.configuration.syntax = orig_syntax end end - -def keyword_args_supported? - RUBY_VERSION >= '2.0.0' && RUBY_ENGINE != 'rbx' -end - -def required_keyword_args_supported? - RUBY_VERSION >= '2.1.0' && RUBY_ENGINE != 'rbx' -end From 87d2f2f383f3d5f55c3dd7d9b5dbb32d70363108 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 26 Jan 2014 17:02:14 -0800 Subject: [PATCH 0443/1179] Remove unused local. --- lib/rspec/mocks/arity_calculator.rb | 2 +- lib/rspec/mocks/ruby_features.rb | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 lib/rspec/mocks/ruby_features.rb diff --git a/lib/rspec/mocks/arity_calculator.rb b/lib/rspec/mocks/arity_calculator.rb index 6f92aceea..75dc0ef8f 100644 --- a/lib/rspec/mocks/arity_calculator.rb +++ b/lib/rspec/mocks/arity_calculator.rb @@ -111,7 +111,7 @@ def max_arity end def parameter_modifier(parameter_type) - mod = method.parameters.any? {|type, _| + method.parameters.any? {|type, _| type == parameter_type } ? 1 : 0 end diff --git a/lib/rspec/mocks/ruby_features.rb b/lib/rspec/mocks/ruby_features.rb new file mode 100644 index 000000000..777a47750 --- /dev/null +++ b/lib/rspec/mocks/ruby_features.rb @@ -0,0 +1,24 @@ +module RSpec + module Mocks + # @api private + # + # Provides query methods for ruby features that differ among + # implementations. + module RubyFeatures + def optional_and_splat_args_supported? + Method.method_defined?(:parameters) + end + module_function :optional_and_splat_args_supported? + + def keyword_args_supported? + RUBY_VERSION >= '2.0.0' && RUBY_ENGINE != 'rbx' + end + module_function :keyword_args_supported? + + def required_keyword_args_supported? + RUBY_VERSION >= '2.1.0' && RUBY_ENGINE != 'rbx' + end + module_function :required_keyword_args_supported? + end + end +end From abf08e85953616a74caf08b116fa080a17c78409 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 26 Jan 2014 17:03:17 -0800 Subject: [PATCH 0444/1179] More specific argument matcher. --- spec/rspec/mocks/verifying_double_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 6fd7c480c..b9156f329 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -224,7 +224,8 @@ module Mocks o = instance_double('LoadedClass', :defined_instance_method => 25) expect { o.defined_instance_method(:a) - }.to raise_error(ArgumentError) + }.to raise_error(ArgumentError, + "Wrong number of arguments. Expected 0, got 1.") end if required_keyword_args_supported? From 8ebdb64acf02c32d8ff3f3235a9f576c9574c526 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 26 Jan 2014 17:08:38 -0800 Subject: [PATCH 0445/1179] Rename ArityCalculator to MethodSignature. --- lib/rspec/mocks/error_generator.rb | 4 +-- ...rity_calculator.rb => method_signature.rb} | 18 ++++++++++--- .../mocks/verifying_message_expecation.rb | 8 +++--- lib/rspec/mocks/verifying_proxy.rb | 6 ++--- ...lator_spec.rb => method_signature_spec.rb} | 26 +++++++++---------- .../verifying_message_expecation_spec.rb | 4 +-- 6 files changed, 39 insertions(+), 27 deletions(-) rename lib/rspec/mocks/{arity_calculator.rb => method_signature.rb} (81%) rename spec/rspec/mocks/{arity_calculator_spec.rb => method_signature_spec.rb} (87%) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 425267bd7..119c28b78 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -81,8 +81,8 @@ def raise_non_public_error(method_name, visibility) end # @private - def raise_arity_error(calculator, actual_args) - __raise calculator.error_description(actual_args) + def raise_arity_error(signature, actual_args) + __raise signature.error_description(actual_args) end # @private diff --git a/lib/rspec/mocks/arity_calculator.rb b/lib/rspec/mocks/method_signature.rb similarity index 81% rename from lib/rspec/mocks/arity_calculator.rb rename to lib/rspec/mocks/method_signature.rb index 75dc0ef8f..477e8f3f0 100644 --- a/lib/rspec/mocks/arity_calculator.rb +++ b/lib/rspec/mocks/method_signature.rb @@ -3,14 +3,26 @@ module RSpec module Mocks - # Figures out the valid arity range for a method. Surprisingly non-trivial. - class ArityCalculator + # Figures out wheter a given method can accept various arguments. + # Surprisingly non-trivial. + # + # The concept of arity here is weird, especially when considering keyword + # arguments. It does not match up with Ruby's `arity` method (which is even + # weirder). The best intepretation of the methods here is "the min/max + # valid length of an array representation of the method arguments." + # + # Since keyword arguments are represented as a hash at this level, they can + # only ever add one to the arity, no matter how many there are. A splatted + # argument will set the maximum arity to infinity. + # + # @api private + class MethodSignature def initialize(method) @method = method end # @api private - def matches?(actual_args) + def accepts?(actual_args) missing_required_keyword_args(actual_args).empty? && within_range?(actual_args.length) end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 8641bfe65..301173dcf 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/arity_calculator' +require 'rspec/mocks/method_signature' module RSpec module Mocks @@ -46,12 +46,12 @@ def ensure_arity!(actual_args) return if method_reference.nil? method_reference.when_defined do |method| - calculator = ArityCalculator.new(method) - unless calculator.matches?(actual_args) + signature = MethodSignature.new(method) + unless signature.accepts?(actual_args) # Fail fast is required, otherwise the message expecation will fail # as well ("expected method not called") and clobber this one. @failed_fast = true - @error_generator.raise_arity_error(calculator, actual_args) + @error_generator.raise_arity_error(signature, actual_args) end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 3b86e9233..6b7a4a52c 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -126,9 +126,9 @@ def proxy_method_invoked(obj, *args, &block) def ensure_arity!(actual_args) @method_reference.when_defined do |method| - calculator = ArityCalculator.new(method) - unless calculator.matches?(actual_args) - raise ArgumentError, calculator.error_description(actual_args) + signature = MethodSignature.new(method) + unless signature.accepts?(actual_args) + raise ArgumentError, signature.error_description(actual_args) end end end diff --git a/spec/rspec/mocks/arity_calculator_spec.rb b/spec/rspec/mocks/method_signature_spec.rb similarity index 87% rename from spec/rspec/mocks/arity_calculator_spec.rb rename to spec/rspec/mocks/method_signature_spec.rb index 4c3e6a3fe..dbd54fbd2 100644 --- a/spec/rspec/mocks/arity_calculator_spec.rb +++ b/spec/rspec/mocks/method_signature_spec.rb @@ -2,16 +2,16 @@ module RSpec module Mocks - describe ArityCalculator do + describe MethodSignature do describe '#verify!' do subject { described_class.new(test_method) } def within_range?(arity) - subject.matches?([nil] * arity) + subject.accepts?([nil] * arity) end - def matches?(args) - subject.matches?(args) + def accepts?(args) + subject.accepts?(args) end def description @@ -104,10 +104,10 @@ def arity_required_kw(x, y:, z:, a: 'default'); end let(:test_method) { method(:arity_required_kw) } it 'returns false unless all required keywords args are present' do - expect(matches?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(matches?([nil, {:a => 0, :y => 1}])).to eq(false) - expect(matches?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(false) - expect(matches?([nil, nil])).to eq(false) + expect(accepts?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(accepts?([nil, {:a => 0, :y => 1}])).to eq(false) + expect(accepts?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(false) + expect(accepts?([nil, nil])).to eq(false) end it 'is described precisely' do @@ -129,11 +129,11 @@ def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end let(:test_method) { method(:arity_required_kw_splat) } it 'returns false unless all required keywords args are present' do - expect(matches?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(matches?([nil, {:a => 0, :y => 1}])).to eq(false) - expect(matches?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(matches?([nil, nil, nil])).to eq(false) - expect(matches?([])).to eq(false) + expect(accepts?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(accepts?([nil, {:a => 0, :y => 1}])).to eq(false) + expect(accepts?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(accepts?([nil, nil, nil])).to eq(false) + expect(accepts?([])).to eq(false) end it 'is described precisely' do diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 4c1745550..3a53b9a46 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -24,7 +24,7 @@ module Mocks args = ["abc123", "xyz987"] subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_arity_error). - with(instance_of(ArityCalculator), args) + with(instance_of(MethodSignature), args) subject.with(*args) end @@ -50,7 +50,7 @@ module Mocks it 'matches arity to 0' do subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_arity_error). - with(instance_of(ArityCalculator), []) + with(instance_of(MethodSignature), []) subject.with(no_args) end From 96005cb03850e662e3194b7f95b8999d8d23519e Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 26 Jan 2014 17:13:56 -0800 Subject: [PATCH 0446/1179] Remove TODO (already done). --- lib/rspec/mocks/method_signature.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rspec/mocks/method_signature.rb b/lib/rspec/mocks/method_signature.rb index 477e8f3f0..a50d943fe 100644 --- a/lib/rspec/mocks/method_signature.rb +++ b/lib/rspec/mocks/method_signature.rb @@ -94,7 +94,6 @@ def max_arity # Method takes a splat argument return INFINITY else - # TODO: Optional keyword args params.count {|(type, _)| ![:block, :keyreq, :key].include?(type) } + parameter_modifier(:key) From 42230d9859dc29d0066737e3118b6d09d4dcb9f6 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 26 Jan 2014 17:20:32 -0800 Subject: [PATCH 0447/1179] Drop coverage threshold to 96%. The drop is due to the addition of more implementation-specific code that has created more lines that are unable to be tested on a single Ruby. --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 12e3119c4..e5f11ffcd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ require 'rspec/mocks/ruby_features' RSpec::Support::Spec.setup_simplecov do - minimum_coverage 97 + minimum_coverage 96 end require 'yaml' From 79c9ce255cad44f82052862f5377ddc130521043 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 27 Jan 2014 18:15:12 -0800 Subject: [PATCH 0448/1179] Support checking for allowed keyword arguments. --- lib/rspec/mocks/method_signature.rb | 121 +++++++++++++--------- spec/rspec/mocks/method_signature_spec.rb | 21 +++- 2 files changed, 91 insertions(+), 51 deletions(-) diff --git a/lib/rspec/mocks/method_signature.rb b/lib/rspec/mocks/method_signature.rb index a50d943fe..10d9dbe4f 100644 --- a/lib/rspec/mocks/method_signature.rb +++ b/lib/rspec/mocks/method_signature.rb @@ -6,15 +6,6 @@ module Mocks # Figures out wheter a given method can accept various arguments. # Surprisingly non-trivial. # - # The concept of arity here is weird, especially when considering keyword - # arguments. It does not match up with Ruby's `arity` method (which is even - # weirder). The best intepretation of the methods here is "the min/max - # valid length of an array representation of the method arguments." - # - # Since keyword arguments are represented as a hash at this level, they can - # only ever add one to the arity, no matter how many there are. A splatted - # argument will set the maximum arity to infinity. - # # @api private class MethodSignature def initialize(method) @@ -22,36 +13,67 @@ def initialize(method) end # @api private - def accepts?(actual_args) - missing_required_keyword_args(actual_args).empty? && - within_range?(actual_args.length) + def accepts?(args) + non_keyword_args, keyword_args = *classify(args) + + (required_keyword_args - keyword_args).empty? && + (keyword_args - allowed_keyword_args).empty? && + within_range?(non_keyword_args.length) end # @api private def error_description(actual_args) - missing = missing_required_keyword_args(actual_args) - if missing.empty? - "Wrong number of arguments. Expected %s, got %s." % [ - range_description, - actual_args.length + non_keyword_args, keyword_args = *classify(actual_args) + + missing_keyword_args = required_keyword_args - keyword_args + invalid_keyword_args = keyword_args - allowed_keyword_args + + if missing_keyword_args.any? + "Missing required keyword arguments: %s" % [ + missing_keyword_args.join(", ") + ] + elsif invalid_keyword_args.any? + "Invalid keyword arguments provided: %s" % [ + invalid_keyword_args.join(", ") ] else - "Missing required keyword arguments: %s" % [ - missing.join(", ") + "Wrong number of arguments. Expected %s, got %s." % [ + range_description, + non_keyword_args.length ] end end private + def method + @method + end + + def classify(args) + keyword_args = if allowed_keyword_args.any? && args.last.is_a?(Hash) + args.pop.keys + else + [] + end + + [args, keyword_args] + end + def range_description - return min_arity.to_s if min_arity == max_arity - return "#{min_arity} or more" if max_arity == INFINITY - "#{min_arity} to #{max_arity}" + if min_non_keyword_args == max_non_keyword_args + return min_non_keyword_args.to_s + end + + if max_non_keyword_args == INFINITY + return "#{min_non_keyword_args} or more" + end + + "#{min_non_keyword_args} to #{max_non_keyword_args}" end def within_range?(actual) - min_arity <= actual && actual <= max_arity + min_non_keyword_args <= actual && actual <= max_non_keyword_args end if RubyFeatures.required_keyword_args_supported? @@ -67,28 +89,18 @@ def missing_required_keyword_args(_) end end - def method - @method - end - if RubyFeatures.optional_and_splat_args_supported? - def required_keyword_args - method.parameters.map {|type, name| - name if type == :keyreq - }.compact - end - - def min_arity + def min_non_keyword_args if method.arity >= 0 method.arity else # `~` inverts the one's complement and gives us the number of # required arguments. ~method.arity - end + parameter_modifier(:keyreq) + end end - def max_arity + def max_non_keyword_args params = method.parameters if params.any? {|(type, _)| type == :rest } # Method takes a splat argument @@ -96,22 +108,18 @@ def max_arity else params.count {|(type, _)| ![:block, :keyreq, :key].include?(type) - } + parameter_modifier(:key) + } end end else - def required_keyword_args - [] - end - - def min_arity + def min_non_keyword_args return method.arity if method.arity >= 0 # `~` inverts the one's complement and gives us the number of # required arguments. ~method.arity end - def max_arity + def max_non_keyword_args # On 1.8, Method#parameters does not exist. There is no way to # distinguish between default and splat args, so there is no way to # have it work correctly for both default and splat args, as far as I @@ -121,10 +129,29 @@ def max_arity end end - def parameter_modifier(parameter_type) - method.parameters.any? {|type, _| - type == parameter_type - } ? 1 : 0 + if RubyFeatures.required_keyword_args_supported? + def required_keyword_args + method.parameters.map {|type, name| + name if type == :keyreq + }.compact + end + else + def required_keyword_args + [] + end + end + + if RubyFeatures.keyword_args_supported? + def allowed_keyword_args + method.parameters.map {|type, name| + name if [:keyreq, :key].include?(type) + }.compact + end + + else + def allowed_keyword_args + [] + end end INFINITY = 1/0.0 diff --git a/spec/rspec/mocks/method_signature_spec.rb b/spec/rspec/mocks/method_signature_spec.rb index dbd54fbd2..805129485 100644 --- a/spec/rspec/mocks/method_signature_spec.rb +++ b/spec/rspec/mocks/method_signature_spec.rb @@ -87,10 +87,23 @@ def arity_kw(x, y:1, z:2); end let(:test_method) { method(:arity_kw) } - it 'returns false unless all required keywords args are present' do + it 'does not require any of the arguments' do expect(within_range?(1)).to eq(true) - expect(within_range?(2)).to eq(true) - expect(within_range?(3)).to eq(false) + expect(within_range?(2)).to eq(false) + end + + it 'does not allow an invalid keyword arguments' do + expect(accepts?([nil, :a => 1])).to eq(false) + end + + it 'is described precisely' do + expect(subject.error_description([nil, {:a => 0, :b => 1}])).to \ + eq("Invalid keyword arguments provided: a, b") + end + + it 'is described precisely when arity is wrong' do + expect(subject.error_description([])).to \ + eq("Wrong number of arguments. Expected 1, got 0.") end end end @@ -117,7 +130,7 @@ def arity_required_kw(x, y:, z:, a: 'default'); end it 'is described precisely when arity is wrong' do expect(subject.error_description([{:z => 0, :y => 1}])).to \ - eq("Wrong number of arguments. Expected 2, got 1.") + eq("Wrong number of arguments. Expected 1, got 0.") end end From 2e203674437dd027eec32a41140e2ec71691d58c Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 27 Jan 2014 18:26:10 -0800 Subject: [PATCH 0449/1179] 1.8 compat --- spec/rspec/mocks/method_signature_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/method_signature_spec.rb b/spec/rspec/mocks/method_signature_spec.rb index 805129485..fc30d8a62 100644 --- a/spec/rspec/mocks/method_signature_spec.rb +++ b/spec/rspec/mocks/method_signature_spec.rb @@ -93,7 +93,7 @@ def arity_kw(x, y:1, z:2); end end it 'does not allow an invalid keyword arguments' do - expect(accepts?([nil, :a => 1])).to eq(false) + expect(accepts?([nil, {:a => 1}])).to eq(false) end it 'is described precisely' do From 013b1c0fc33c5daae6f3b6f585a578a5188c1a50 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Tue, 28 Jan 2014 08:41:14 -0800 Subject: [PATCH 0450/1179] Refactor MethodSignature to MethodSignatureVerifier. --- lib/rspec/mocks/error_generator.rb | 4 +- ...nature.rb => method_signature_verifier.rb} | 100 +++++++++--------- .../mocks/verifying_message_expecation.rb | 8 +- lib/rspec/mocks/verifying_proxy.rb | 6 +- ...c.rb => method_signature_verifier_spec.rb} | 78 +++++++------- .../verifying_message_expecation_spec.rb | 4 +- 6 files changed, 102 insertions(+), 98 deletions(-) rename lib/rspec/mocks/{method_signature.rb => method_signature_verifier.rb} (72%) rename spec/rspec/mocks/{method_signature_spec.rb => method_signature_verifier_spec.rb} (62%) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 119c28b78..cd368ffef 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -81,8 +81,8 @@ def raise_non_public_error(method_name, visibility) end # @private - def raise_arity_error(signature, actual_args) - __raise signature.error_description(actual_args) + def raise_arity_error(verifier) + __raise verifier.error end # @private diff --git a/lib/rspec/mocks/method_signature.rb b/lib/rspec/mocks/method_signature_verifier.rb similarity index 72% rename from lib/rspec/mocks/method_signature.rb rename to lib/rspec/mocks/method_signature_verifier.rb index 10d9dbe4f..de503ac0b 100644 --- a/lib/rspec/mocks/method_signature.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -7,27 +7,21 @@ module Mocks # Surprisingly non-trivial. # # @api private - class MethodSignature - def initialize(method) + class MethodSignatureVerifier + def initialize(method, args) @method = method + @args = args end # @api private - def accepts?(args) - non_keyword_args, keyword_args = *classify(args) - - (required_keyword_args - keyword_args).empty? && - (keyword_args - allowed_keyword_args).empty? && - within_range?(non_keyword_args.length) + def valid? + missing_keyword_args.empty? && + invalid_keyword_args.empty? && + valid_non_keyword_args? end # @api private - def error_description(actual_args) - non_keyword_args, keyword_args = *classify(actual_args) - - missing_keyword_args = required_keyword_args - keyword_args - invalid_keyword_args = keyword_args - allowed_keyword_args - + def error if missing_keyword_args.any? "Missing required keyword arguments: %s" % [ missing_keyword_args.join(", ") @@ -36,9 +30,9 @@ def error_description(actual_args) "Invalid keyword arguments provided: %s" % [ invalid_keyword_args.join(", ") ] - else + elsif !valid_non_keyword_args? "Wrong number of arguments. Expected %s, got %s." % [ - range_description, + non_kw_args_error, non_keyword_args.length ] end @@ -50,17 +44,40 @@ def method @method end - def classify(args) - keyword_args = if allowed_keyword_args.any? && args.last.is_a?(Hash) - args.pop.keys - else - [] - end + def valid_non_keyword_args? + actual = non_keyword_args.length + min_non_keyword_args <= actual && actual <= max_non_keyword_args + end + + def non_keyword_args + split_args(@args)[0] + end + + def keyword_args + split_args(@args)[1] + end + + def missing_keyword_args + required_keyword_args - keyword_args + end + + def invalid_keyword_args + keyword_args - allowed_keyword_args + end + + def split_args(args) + @split_args ||= begin + keyword_args = if allowed_keyword_args.any? && args.last.is_a?(Hash) + args.pop.keys + else + [] + end - [args, keyword_args] + [args, keyword_args] + end end - def range_description + def non_kw_args_error if min_non_keyword_args == max_non_keyword_args return min_non_keyword_args.to_s end @@ -72,23 +89,6 @@ def range_description "#{min_non_keyword_args} to #{max_non_keyword_args}" end - def within_range?(actual) - min_non_keyword_args <= actual && actual <= max_non_keyword_args - end - - if RubyFeatures.required_keyword_args_supported? - def missing_required_keyword_args(actual_args) - keyword_args = actual_args.last - keyword_args = {} unless keyword_args.is_a?(Hash) - - required_keyword_args - keyword_args.keys - end - else - def missing_required_keyword_args(_) - [] - end - end - if RubyFeatures.optional_and_splat_args_supported? def min_non_keyword_args if method.arity >= 0 @@ -129,27 +129,27 @@ def max_non_keyword_args end end - if RubyFeatures.required_keyword_args_supported? - def required_keyword_args + if RubyFeatures.keyword_args_supported? + def allowed_keyword_args method.parameters.map {|type, name| - name if type == :keyreq + name if [:keyreq, :key].include?(type) }.compact end + else - def required_keyword_args + def allowed_keyword_args [] end end - if RubyFeatures.keyword_args_supported? - def allowed_keyword_args + if RubyFeatures.required_keyword_args_supported? + def required_keyword_args method.parameters.map {|type, name| - name if [:keyreq, :key].include?(type) + name if type == :keyreq }.compact end - else - def allowed_keyword_args + def required_keyword_args [] end end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 301173dcf..603f007c2 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/method_signature' +require 'rspec/mocks/method_signature_verifier' module RSpec module Mocks @@ -46,12 +46,12 @@ def ensure_arity!(actual_args) return if method_reference.nil? method_reference.when_defined do |method| - signature = MethodSignature.new(method) - unless signature.accepts?(actual_args) + verifier = MethodSignatureVerifier.new(method, actual_args) + unless verifier.valid? # Fail fast is required, otherwise the message expecation will fail # as well ("expected method not called") and clobber this one. @failed_fast = true - @error_generator.raise_arity_error(signature, actual_args) + @error_generator.raise_arity_error(verifier) end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 6b7a4a52c..c736db0c0 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -126,9 +126,9 @@ def proxy_method_invoked(obj, *args, &block) def ensure_arity!(actual_args) @method_reference.when_defined do |method| - signature = MethodSignature.new(method) - unless signature.accepts?(actual_args) - raise ArgumentError, signature.error_description(actual_args) + verifier = MethodSignatureVerifier.new(method, actual_args) + unless verifier.valid? + raise ArgumentError, verifier.error end end end diff --git a/spec/rspec/mocks/method_signature_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb similarity index 62% rename from spec/rspec/mocks/method_signature_spec.rb rename to spec/rspec/mocks/method_signature_verifier_spec.rb index fc30d8a62..8c6ec856a 100644 --- a/spec/rspec/mocks/method_signature_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -2,20 +2,24 @@ module RSpec module Mocks - describe MethodSignature do + describe MethodSignatureVerifier do describe '#verify!' do subject { described_class.new(test_method) } - def within_range?(arity) - subject.accepts?([nil] * arity) + def valid_non_kw_args?(arity) + described_class.new(test_method, [nil] * arity).valid? end - def accepts?(args) - subject.accepts?(args) + def valid?(args) + described_class.new(test_method, args).valid? end def description - subject.error_description([])[/Expected (.*),/, 1] + described_class.new(test_method, []).error[/Expected (.*),/, 1] + end + + def error_for(args) + described_class.new(test_method, args).error end describe 'with a method with arguments' do @@ -24,9 +28,9 @@ def arity_two(x, y); end let(:test_method) { method(:arity_two) } it 'covers only the exact arity' do - expect(within_range?(1)).to eq(false) - expect(within_range?(2)).to eq(true) - expect(within_range?(3)).to eq(false) + expect(valid_non_kw_args?(1)).to eq(false) + expect(valid_non_kw_args?(2)).to eq(true) + expect(valid_non_kw_args?(3)).to eq(false) end it 'is described precisely' do @@ -40,10 +44,10 @@ def arity_splat(_, *); end let(:test_method) { method(:arity_splat) } it 'covers a range from the lower bound upwards' do - expect(within_range?(0)).to eq(false) - expect(within_range?(1)).to eq(true) - expect(within_range?(2)).to eq(true) - expect(within_range?(3)).to eq(true) + expect(valid_non_kw_args?(0)).to eq(false) + expect(valid_non_kw_args?(1)).to eq(true) + expect(valid_non_kw_args?(2)).to eq(true) + expect(valid_non_kw_args?(3)).to eq(true) end it 'is described with no upper bound' do @@ -57,14 +61,14 @@ def arity_optional(x, y, z = 1); end let(:test_method) { method(:arity_optional) } it 'covers a range from min to max possible arguments' do - expect(within_range?(1)).to eq(false) - expect(within_range?(2)).to eq(true) - expect(within_range?(3)).to eq(true) + expect(valid_non_kw_args?(1)).to eq(false) + expect(valid_non_kw_args?(2)).to eq(true) + expect(valid_non_kw_args?(3)).to eq(true) if optional_and_splat_args_supported? - expect(within_range?(4)).to eq(false) + expect(valid_non_kw_args?(4)).to eq(false) else - expect(within_range?(4)).to eq(true) + expect(valid_non_kw_args?(4)).to eq(true) end end @@ -88,21 +92,21 @@ def arity_kw(x, y:1, z:2); end let(:test_method) { method(:arity_kw) } it 'does not require any of the arguments' do - expect(within_range?(1)).to eq(true) - expect(within_range?(2)).to eq(false) + expect(valid?([nil])).to eq(true) + expect(valid?([nil, nil])).to eq(false) end it 'does not allow an invalid keyword arguments' do - expect(accepts?([nil, {:a => 1}])).to eq(false) + expect(valid?([nil, {:a => 1}])).to eq(false) end it 'is described precisely' do - expect(subject.error_description([nil, {:a => 0, :b => 1}])).to \ + expect(error_for([nil, {:a => 0, :b => 1}])).to \ eq("Invalid keyword arguments provided: a, b") end it 'is described precisely when arity is wrong' do - expect(subject.error_description([])).to \ + expect(error_for([])).to \ eq("Wrong number of arguments. Expected 1, got 0.") end end @@ -117,19 +121,19 @@ def arity_required_kw(x, y:, z:, a: 'default'); end let(:test_method) { method(:arity_required_kw) } it 'returns false unless all required keywords args are present' do - expect(accepts?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(accepts?([nil, {:a => 0, :y => 1}])).to eq(false) - expect(accepts?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(false) - expect(accepts?([nil, nil])).to eq(false) + expect(valid?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(valid?([nil, {:a => 0, :y => 1}])).to eq(false) + expect(valid?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(false) + expect(valid?([nil, nil])).to eq(false) end it 'is described precisely' do - expect(subject.error_description([nil, {:a => 0}])).to \ + expect(error_for([nil, {:a => 0}])).to \ eq("Missing required keyword arguments: y, z") end it 'is described precisely when arity is wrong' do - expect(subject.error_description([{:z => 0, :y => 1}])).to \ + expect(error_for([{:z => 0, :y => 1}])).to \ eq("Wrong number of arguments. Expected 1, got 0.") end end @@ -142,15 +146,15 @@ def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end let(:test_method) { method(:arity_required_kw_splat) } it 'returns false unless all required keywords args are present' do - expect(accepts?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(accepts?([nil, {:a => 0, :y => 1}])).to eq(false) - expect(accepts?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(accepts?([nil, nil, nil])).to eq(false) - expect(accepts?([])).to eq(false) + expect(valid?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(valid?([nil, {:a => 0, :y => 1}])).to eq(false) + expect(valid?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) + expect(valid?([nil, nil, nil])).to eq(false) + expect(valid?([])).to eq(false) end it 'is described precisely' do - expect(subject.error_description([nil, {:y => 1}])).to \ + expect(error_for([nil, {:y => 1}])).to \ eq("Missing required keyword arguments: z") end end @@ -162,8 +166,8 @@ def arity_block(_, &block); end let(:test_method) { method(:arity_block) } it 'does not count the block as a parameter' do - expect(within_range?(1)).to eq(true) - expect(within_range?(2)).to eq(false) + expect(valid_non_kw_args?(1)).to eq(true) + expect(valid_non_kw_args?(2)).to eq(false) end it 'is described precisely' do diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 3a53b9a46..a5060e3b3 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -24,7 +24,7 @@ module Mocks args = ["abc123", "xyz987"] subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_arity_error). - with(instance_of(MethodSignature), args) + with(instance_of(MethodSignatureVerifier)) subject.with(*args) end @@ -50,7 +50,7 @@ module Mocks it 'matches arity to 0' do subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_arity_error). - with(instance_of(MethodSignature), []) + with(instance_of(MethodSignatureVerifier)) subject.with(no_args) end From ad6a7e6c6c039ff9c1e24af2bb62b1bbe55150f3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 28 Jan 2014 18:31:33 -0800 Subject: [PATCH 0451/1179] Standardize on kw_args over keyword_args. It's a bit shorter but still obvious what it means. --- lib/rspec/mocks/method_signature_verifier.rb | 70 +++++++++---------- lib/rspec/mocks/ruby_features.rb | 8 +-- .../mocks/method_signature_verifier_spec.rb | 4 +- spec/rspec/mocks/verifying_double_spec.rb | 4 +- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index de503ac0b..f1ec06aa2 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -15,25 +15,25 @@ def initialize(method, args) # @api private def valid? - missing_keyword_args.empty? && - invalid_keyword_args.empty? && - valid_non_keyword_args? + missing_kw_args.empty? && + invalid_kw_args.empty? && + valid_non_kw_args? end # @api private def error - if missing_keyword_args.any? + if missing_kw_args.any? "Missing required keyword arguments: %s" % [ - missing_keyword_args.join(", ") + missing_kw_args.join(", ") ] - elsif invalid_keyword_args.any? + elsif invalid_kw_args.any? "Invalid keyword arguments provided: %s" % [ - invalid_keyword_args.join(", ") + invalid_kw_args.join(", ") ] - elsif !valid_non_keyword_args? + elsif !valid_non_kw_args? "Wrong number of arguments. Expected %s, got %s." % [ non_kw_args_error, - non_keyword_args.length + non_kw_args.length ] end end @@ -44,53 +44,53 @@ def method @method end - def valid_non_keyword_args? - actual = non_keyword_args.length - min_non_keyword_args <= actual && actual <= max_non_keyword_args + def valid_non_kw_args? + actual = non_kw_args.length + min_non_kw_args <= actual && actual <= max_non_kw_args end - def non_keyword_args + def non_kw_args split_args(@args)[0] end - def keyword_args + def kw_args split_args(@args)[1] end - def missing_keyword_args - required_keyword_args - keyword_args + def missing_kw_args + required_kw_args - kw_args end - def invalid_keyword_args - keyword_args - allowed_keyword_args + def invalid_kw_args + kw_args - allowed_kw_args end def split_args(args) @split_args ||= begin - keyword_args = if allowed_keyword_args.any? && args.last.is_a?(Hash) + kw_args = if allowed_kw_args.any? && args.last.is_a?(Hash) args.pop.keys else [] end - [args, keyword_args] + [args, kw_args] end end def non_kw_args_error - if min_non_keyword_args == max_non_keyword_args - return min_non_keyword_args.to_s + if min_non_kw_args == max_non_kw_args + return min_non_kw_args.to_s end - if max_non_keyword_args == INFINITY - return "#{min_non_keyword_args} or more" + if max_non_kw_args == INFINITY + return "#{min_non_kw_args} or more" end - "#{min_non_keyword_args} to #{max_non_keyword_args}" + "#{min_non_kw_args} to #{max_non_kw_args}" end if RubyFeatures.optional_and_splat_args_supported? - def min_non_keyword_args + def min_non_kw_args if method.arity >= 0 method.arity else @@ -100,7 +100,7 @@ def min_non_keyword_args end end - def max_non_keyword_args + def max_non_kw_args params = method.parameters if params.any? {|(type, _)| type == :rest } # Method takes a splat argument @@ -112,14 +112,14 @@ def max_non_keyword_args end end else - def min_non_keyword_args + def min_non_kw_args return method.arity if method.arity >= 0 # `~` inverts the one's complement and gives us the number of # required arguments. ~method.arity end - def max_non_keyword_args + def max_non_kw_args # On 1.8, Method#parameters does not exist. There is no way to # distinguish between default and splat args, so there is no way to # have it work correctly for both default and splat args, as far as I @@ -129,27 +129,27 @@ def max_non_keyword_args end end - if RubyFeatures.keyword_args_supported? - def allowed_keyword_args + if RubyFeatures.kw_args_supported? + def allowed_kw_args method.parameters.map {|type, name| name if [:keyreq, :key].include?(type) }.compact end else - def allowed_keyword_args + def allowed_kw_args [] end end - if RubyFeatures.required_keyword_args_supported? - def required_keyword_args + if RubyFeatures.required_kw_args_supported? + def required_kw_args method.parameters.map {|type, name| name if type == :keyreq }.compact end else - def required_keyword_args + def required_kw_args [] end end diff --git a/lib/rspec/mocks/ruby_features.rb b/lib/rspec/mocks/ruby_features.rb index 777a47750..cdad92505 100644 --- a/lib/rspec/mocks/ruby_features.rb +++ b/lib/rspec/mocks/ruby_features.rb @@ -10,15 +10,15 @@ def optional_and_splat_args_supported? end module_function :optional_and_splat_args_supported? - def keyword_args_supported? + def kw_args_supported? RUBY_VERSION >= '2.0.0' && RUBY_ENGINE != 'rbx' end - module_function :keyword_args_supported? + module_function :kw_args_supported? - def required_keyword_args_supported? + def required_kw_args_supported? RUBY_VERSION >= '2.1.0' && RUBY_ENGINE != 'rbx' end - module_function :required_keyword_args_supported? + module_function :required_kw_args_supported? end end end diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index 8c6ec856a..f7a3ba793 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -83,7 +83,7 @@ def arity_optional(x, y, z = 1); end end end - if keyword_args_supported? + if kw_args_supported? describe 'a method with optional keyword arguments' do eval <<-RUBY def arity_kw(x, y:1, z:2); end @@ -112,7 +112,7 @@ def arity_kw(x, y:1, z:2); end end end - if required_keyword_args_supported? + if required_kw_args_supported? describe 'a method with required keyword arguments' do eval <<-RUBY def arity_required_kw(x, y:, z:, a: 'default'); end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index b9156f329..ee3ff18ad 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -36,7 +36,7 @@ def defined_private_class_method def defined_instance_method end - if required_keyword_args_supported? + if required_kw_args_supported? # Need to eval this since it is invalid syntax on earlier rubies. eval <<-RUBY def kw_args_method(optional_arg:'hello', required_arg:) @@ -228,7 +228,7 @@ module Mocks "Wrong number of arguments. Expected 0, got 1.") end - if required_keyword_args_supported? + if required_kw_args_supported? it 'allows keyword arguments' do o = instance_double('LoadedClass', :kw_args_method => true) expect(o.kw_args_method(:required_arg => 'something')).to eq(true) From 83f2e312355fc28632987fd752fc06466bbb750c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 28 Jan 2014 18:35:21 -0800 Subject: [PATCH 0452/1179] Refactoring: extract MethodSignature class. - The signature of a method is a concept in its own right and deserves to be represented as its own object. - This gives a clear delineaziation of responsibilities between extracting method signature info and verifying arguments against that info. - The new `classify_parameters` approach should perform a bit better than what we had before; it iterates over `method.parameters` once total where as before it would iterate over it 3 times. --- lib/rspec/mocks/method_signature_verifier.rb | 173 +++++++++---------- 1 file changed, 82 insertions(+), 91 deletions(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index f1ec06aa2..e6e742a8a 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -2,6 +2,80 @@ module RSpec module Mocks + # Extracts info about the number of arguments and allowed/required + # keyword args of a given method. + # + # @api private + class MethodSignature + attr_reader :min_non_kw_args, :max_non_kw_args + + def initialize(method) + @method = method + classify_parameters + end + + def non_kw_args_error + if min_non_kw_args == max_non_kw_args + return min_non_kw_args.to_s + end + + if max_non_kw_args == INFINITY + return "#{min_non_kw_args} or more" + end + + "#{min_non_kw_args} to #{max_non_kw_args}" + end + + if RubyFeatures.optional_and_splat_args_supported? + attr_reader :allowed_kw_args, :required_kw_args + + def classify_parameters + optional_non_kw_args = @min_non_kw_args = 0 + optional_kw_args, @required_kw_args = [], [] + + @method.parameters.each do |(type, name)| + case type + # def foo(a:) + when :keyreq then @required_kw_args << name + # def foo(a: 1) + when :key then optional_kw_args << name + # def foo(a) + when :req then @min_non_kw_args += 1 + # def foo(a = 1) + when :opt then optional_non_kw_args += 1 + # def foo(*a) + when :rest then optional_non_kw_args = INFINITY + end + end + + @max_non_kw_args = @min_non_kw_args + optional_non_kw_args + @allowed_kw_args = @required_kw_args + optional_kw_args + end + else + def allowed_kw_args + [] + end + + def required_kw_args + [] + end + + def classify_parameters + arity = @method.arity + if arity < 0 + # `~` inverts the one's complement and gives us the + # number of required args + @min_non_kw_args = ~arity + @max_non_kw_args = INFINITY + else + @min_non_kw_args = arity + @max_non_kw_args = arity + end + end + end + + INFINITY = 1/0.0 + end # Figures out wheter a given method can accept various arguments. # Surprisingly non-trivial. @@ -9,8 +83,8 @@ module Mocks # @api private class MethodSignatureVerifier def initialize(method, args) - @method = method - @args = args + @signature = MethodSignature.new(method) + @args = args end # @api private @@ -32,21 +106,17 @@ def error ] elsif !valid_non_kw_args? "Wrong number of arguments. Expected %s, got %s." % [ - non_kw_args_error, + @signature.non_kw_args_error, non_kw_args.length ] end end - private - - def method - @method - end + private def valid_non_kw_args? actual = non_kw_args.length - min_non_kw_args <= actual && actual <= max_non_kw_args + @signature.min_non_kw_args <= actual && actual <= @signature.max_non_kw_args end def non_kw_args @@ -58,16 +128,16 @@ def kw_args end def missing_kw_args - required_kw_args - kw_args + @signature.required_kw_args - kw_args end def invalid_kw_args - kw_args - allowed_kw_args + kw_args - @signature.allowed_kw_args end def split_args(args) @split_args ||= begin - kw_args = if allowed_kw_args.any? && args.last.is_a?(Hash) + kw_args = if @signature.allowed_kw_args.any? && args.last.is_a?(Hash) args.pop.keys else [] @@ -76,85 +146,6 @@ def split_args(args) [args, kw_args] end end - - def non_kw_args_error - if min_non_kw_args == max_non_kw_args - return min_non_kw_args.to_s - end - - if max_non_kw_args == INFINITY - return "#{min_non_kw_args} or more" - end - - "#{min_non_kw_args} to #{max_non_kw_args}" - end - - if RubyFeatures.optional_and_splat_args_supported? - def min_non_kw_args - if method.arity >= 0 - method.arity - else - # `~` inverts the one's complement and gives us the number of - # required arguments. - ~method.arity - end - end - - def max_non_kw_args - params = method.parameters - if params.any? {|(type, _)| type == :rest } - # Method takes a splat argument - return INFINITY - else - params.count {|(type, _)| - ![:block, :keyreq, :key].include?(type) - } - end - end - else - def min_non_kw_args - return method.arity if method.arity >= 0 - # `~` inverts the one's complement and gives us the number of - # required arguments. - ~method.arity - end - - def max_non_kw_args - # On 1.8, Method#parameters does not exist. There is no way to - # distinguish between default and splat args, so there is no way to - # have it work correctly for both default and splat args, as far as I - # can tell. The best we can do is consider it INFINITY (to be - # tolerant of splat args). - method.arity < 0 ? INFINITY : method.arity - end - end - - if RubyFeatures.kw_args_supported? - def allowed_kw_args - method.parameters.map {|type, name| - name if [:keyreq, :key].include?(type) - }.compact - end - - else - def allowed_kw_args - [] - end - end - - if RubyFeatures.required_kw_args_supported? - def required_kw_args - method.parameters.map {|type, name| - name if type == :keyreq - }.compact - end - else - def required_kw_args - [] - end - end - - INFINITY = 1/0.0 end end end From ecca155555799908eba9fe1ddd6d929fc2d2e73f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 28 Jan 2014 21:25:01 -0800 Subject: [PATCH 0453/1179] Add missing test. A last-arg hash should only be treated as kw args if the method has kw arg params. --- spec/rspec/mocks/method_signature_verifier_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index f7a3ba793..84d83dc27 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -33,6 +33,10 @@ def arity_two(x, y); end expect(valid_non_kw_args?(3)).to eq(false) end + it 'does not treat a last-arg hash as kw args' do + expect(valid?([1, {}])).to eq(true) + end + it 'is described precisely' do expect(description).to eq("2") end From 4a8a003a2769a7052694ba5217ae426d201e1536 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 08:19:31 -0800 Subject: [PATCH 0454/1179] Clean up `split_args` a bit. - It was odd to have it memoize internally but accept an arg. - Simpler just to call it once in `initialize`. --- lib/rspec/mocks/method_signature_verifier.rb | 27 ++++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index e6e742a8a..62ded4e06 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -82,9 +82,12 @@ def classify_parameters # # @api private class MethodSignatureVerifier + # @api private + attr_reader :non_kw_args, :kw_args + def initialize(method, args) @signature = MethodSignature.new(method) - @args = args + @non_kw_args, @kw_args = split_args(args) end # @api private @@ -119,14 +122,6 @@ def valid_non_kw_args? @signature.min_non_kw_args <= actual && actual <= @signature.max_non_kw_args end - def non_kw_args - split_args(@args)[0] - end - - def kw_args - split_args(@args)[1] - end - def missing_kw_args @signature.required_kw_args - kw_args end @@ -136,15 +131,13 @@ def invalid_kw_args end def split_args(args) - @split_args ||= begin - kw_args = if @signature.allowed_kw_args.any? && args.last.is_a?(Hash) - args.pop.keys - else - [] - end - - [args, kw_args] + kw_args = if @signature.allowed_kw_args.any? && args.last.is_a?(Hash) + args.pop.keys + else + [] end + + [args, kw_args] end end end From 3769d40ad562600fcdc3396d1d16c65f90828608 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 08:28:22 -0800 Subject: [PATCH 0455/1179] Decrease syntactic noise of argument lists. I find this: foo(nil, :a => 1) ...to be much less noisy than this: foo([nil, { :a => 1 }]) The first form more closely matches how people pass args and kw args to methods, anyway. --- .../mocks/method_signature_verifier_spec.rb | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index 84d83dc27..aae47e9de 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -10,7 +10,7 @@ def valid_non_kw_args?(arity) described_class.new(test_method, [nil] * arity).valid? end - def valid?(args) + def valid?(*args) described_class.new(test_method, args).valid? end @@ -18,7 +18,7 @@ def description described_class.new(test_method, []).error[/Expected (.*),/, 1] end - def error_for(args) + def error_for(*args) described_class.new(test_method, args).error end @@ -34,7 +34,7 @@ def arity_two(x, y); end end it 'does not treat a last-arg hash as kw args' do - expect(valid?([1, {}])).to eq(true) + expect(valid?(1, {})).to eq(true) end it 'is described precisely' do @@ -96,21 +96,21 @@ def arity_kw(x, y:1, z:2); end let(:test_method) { method(:arity_kw) } it 'does not require any of the arguments' do - expect(valid?([nil])).to eq(true) - expect(valid?([nil, nil])).to eq(false) + expect(valid?(nil)).to eq(true) + expect(valid?(nil, nil)).to eq(false) end it 'does not allow an invalid keyword arguments' do - expect(valid?([nil, {:a => 1}])).to eq(false) + expect(valid?(nil, :a => 1)).to eq(false) end it 'is described precisely' do - expect(error_for([nil, {:a => 0, :b => 1}])).to \ + expect(error_for(nil, :a => 0, :b => 1)).to \ eq("Invalid keyword arguments provided: a, b") end it 'is described precisely when arity is wrong' do - expect(error_for([])).to \ + expect(error_for()).to \ eq("Wrong number of arguments. Expected 1, got 0.") end end @@ -125,19 +125,19 @@ def arity_required_kw(x, y:, z:, a: 'default'); end let(:test_method) { method(:arity_required_kw) } it 'returns false unless all required keywords args are present' do - expect(valid?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(valid?([nil, {:a => 0, :y => 1}])).to eq(false) - expect(valid?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(false) - expect(valid?([nil, nil])).to eq(false) + expect(valid?(nil, :a => 0, :y => 1, :z => 2)).to eq(true) + expect(valid?(nil, :a => 0, :y => 1)).to eq(false) + expect(valid?(nil, nil, :a => 0, :y => 1, :z => 2)).to eq(false) + expect(valid?(nil, nil)).to eq(false) end it 'is described precisely' do - expect(error_for([nil, {:a => 0}])).to \ + expect(error_for(nil, :a => 0)).to \ eq("Missing required keyword arguments: y, z") end it 'is described precisely when arity is wrong' do - expect(error_for([{:z => 0, :y => 1}])).to \ + expect(error_for(:z => 0, :y => 1)).to \ eq("Wrong number of arguments. Expected 1, got 0.") end end @@ -150,15 +150,15 @@ def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end let(:test_method) { method(:arity_required_kw_splat) } it 'returns false unless all required keywords args are present' do - expect(valid?([nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(valid?([nil, {:a => 0, :y => 1}])).to eq(false) - expect(valid?([nil, nil, {:a => 0, :y => 1, :z => 2}])).to eq(true) - expect(valid?([nil, nil, nil])).to eq(false) - expect(valid?([])).to eq(false) + expect(valid?(nil, :a => 0, :y => 1, :z => 2)).to eq(true) + expect(valid?(nil, :a => 0, :y => 1)).to eq(false) + expect(valid?(nil, nil, :a => 0, :y => 1, :z => 2)).to eq(true) + expect(valid?(nil, nil, nil)).to eq(false) + expect(valid?).to eq(false) end it 'is described precisely' do - expect(error_for([nil, {:y => 1}])).to \ + expect(error_for(nil, :y => 1)).to \ eq("Missing required keyword arguments: z") end end From 538409954049d287032102b669481f5ad9b0807e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 09:11:27 -0800 Subject: [PATCH 0456/1179] Refactor: prep for supporting `**kw_args`. To support `**kw_args`, `allowed_kw_args` will have to act like an infinite set. Rather than making the verifier (and any future clients) responsible for dealing with this special case, it's simpler to move the invalid/missing kw arg calculations into `MethodSignature`, where it can handle that special case internally. --- lib/rspec/mocks/method_signature_verifier.rb | 26 +++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 62ded4e06..2c75cfe29 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -27,7 +27,17 @@ def non_kw_args_error end if RubyFeatures.optional_and_splat_args_supported? - attr_reader :allowed_kw_args, :required_kw_args + def missing_kw_args_from(given_kw_args) + @required_kw_args - given_kw_args + end + + def invalid_kw_args_from(given_kw_args) + given_kw_args - @allowed_kw_args + end + + def has_kw_args_in?(args) + args.last.is_a?(Hash) && @allowed_kw_args.any? + end def classify_parameters optional_non_kw_args = @min_non_kw_args = 0 @@ -52,14 +62,18 @@ def classify_parameters @allowed_kw_args = @required_kw_args + optional_kw_args end else - def allowed_kw_args + def missing_kw_args_from(given_kw_args) [] end - def required_kw_args + def invalid_kw_args_from(given_kw_args) [] end + def has_kw_args_in?(args) + false + end + def classify_parameters arity = @method.arity if arity < 0 @@ -123,15 +137,15 @@ def valid_non_kw_args? end def missing_kw_args - @signature.required_kw_args - kw_args + @signature.missing_kw_args_from(kw_args) end def invalid_kw_args - kw_args - @signature.allowed_kw_args + @signature.invalid_kw_args_from(kw_args) end def split_args(args) - kw_args = if @signature.allowed_kw_args.any? && args.last.is_a?(Hash) + kw_args = if @signature.has_kw_args_in?(args) args.pop.keys else [] From 7e997eb8ed9eeb3345f1103baa65399251653f6c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 09:58:12 -0800 Subject: [PATCH 0457/1179] Add support for `**kw_args` parameters. --- lib/rspec/mocks/method_signature_verifier.rb | 19 +++++--- .../mocks/method_signature_verifier_spec.rb | 46 ++++++++++++++++++- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 2c75cfe29..265a545ae 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -32,29 +32,36 @@ def missing_kw_args_from(given_kw_args) end def invalid_kw_args_from(given_kw_args) + return [] if @allows_any_kw_args given_kw_args - @allowed_kw_args end def has_kw_args_in?(args) - args.last.is_a?(Hash) && @allowed_kw_args.any? + return false unless args.last.is_a?(Hash) + return false if args.count <= min_non_kw_args + + @allows_any_kw_args || @allowed_kw_args.any? end def classify_parameters optional_non_kw_args = @min_non_kw_args = 0 optional_kw_args, @required_kw_args = [], [] + @allows_any_kw_args = false @method.parameters.each do |(type, name)| case type # def foo(a:) - when :keyreq then @required_kw_args << name + when :keyreq then @required_kw_args << name # def foo(a: 1) - when :key then optional_kw_args << name + when :key then optional_kw_args << name + # def foo(**kw_args) + when :keyrest then @allows_any_kw_args = true # def foo(a) - when :req then @min_non_kw_args += 1 + when :req then @min_non_kw_args += 1 # def foo(a = 1) - when :opt then optional_non_kw_args += 1 + when :opt then optional_non_kw_args += 1 # def foo(*a) - when :rest then optional_non_kw_args = INFINITY + when :rest then optional_non_kw_args = INFINITY end end diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index aae47e9de..4bd96557f 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -137,8 +137,8 @@ def arity_required_kw(x, y:, z:, a: 'default'); end end it 'is described precisely when arity is wrong' do - expect(error_for(:z => 0, :y => 1)).to \ - eq("Wrong number of arguments. Expected 1, got 0.") + expect(error_for(nil, nil, :z => 0, :y => 1)).to \ + eq("Wrong number of arguments. Expected 1, got 2.") end end @@ -162,6 +162,48 @@ def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end eq("Missing required keyword arguments: z") end end + + describe 'a method with required keyword arguments and a keyword arg splat' do + eval <<-RUBY + def arity_kw_arg_splat(x:, **rest); end + RUBY + + let(:test_method) { method(:arity_kw_arg_splat) } + + it 'allows extra undeclared keyword args' do + expect(valid?(:x => 1)).to eq(true) + expect(valid?(:x => 1, :y => 2)).to eq(true) + end + + it 'is described precisely' do + expect(error_for(:y => 1)).to \ + eq("Missing required keyword arguments: x") + end + end + + describe 'a method with a required arg and a keyword arg splat' do + eval <<-RUBY + def arity_kw_arg_splat(x, **rest); end + RUBY + + let(:test_method) { method(:arity_kw_arg_splat) } + + it 'allows a single arg and any number of keyword args' do + expect(valid?(nil)).to eq(true) + expect(valid?(nil, :x => 1)).to eq(true) + expect(valid?(nil, :x => 1, :y => 2)).to eq(true) + expect(valid?(:x => 1)).to eq(true) + + expect(valid?).to eq(false) + expect(valid?(nil, nil)).to eq(false) + expect(valid?(nil, nil, :x => 1)).to eq(false) + end + + it 'is described precisely' do + expect(error_for()).to \ + eq("Wrong number of arguments. Expected 1, got 0.") + end + end end describe 'a method with a block' do From 86b2a68b1d8b379c91c5202d1d3bfe8e38caca53 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 10:01:59 -0800 Subject: [PATCH 0458/1179] We can't count on `is_a?` working on every argument. BasicObject, for example, does not respond to it. It's safer to use `SomeClass === obj` than `obj.is_a?(SomeClass)`. --- lib/rspec/mocks/method_signature_verifier.rb | 2 +- spec/rspec/mocks/method_signature_verifier_spec.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 265a545ae..ed1d8807e 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -37,7 +37,7 @@ def invalid_kw_args_from(given_kw_args) end def has_kw_args_in?(args) - return false unless args.last.is_a?(Hash) + return false unless Hash === args.last return false if args.count <= min_non_kw_args @allows_any_kw_args || @allowed_kw_args.any? diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index 4bd96557f..fb33d93a7 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -113,6 +113,10 @@ def arity_kw(x, y:1, z:2); end expect(error_for()).to \ eq("Wrong number of arguments. Expected 1, got 0.") end + + it 'does not blow up when given a BasicObject as the last arg' do + expect(valid?(BasicObject.new)).to eq(true) + end end end From 8e1ac5f470d7ad02766c46397816151995ee3807 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 10:20:13 -0800 Subject: [PATCH 0459/1179] Don't mutate the provided args array. Mutating could lead to surprises for the caller. --- lib/rspec/mocks/method_signature_verifier.rb | 4 ++-- spec/rspec/mocks/method_signature_verifier_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index ed1d8807e..562e87993 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -108,7 +108,7 @@ class MethodSignatureVerifier def initialize(method, args) @signature = MethodSignature.new(method) - @non_kw_args, @kw_args = split_args(args) + @non_kw_args, @kw_args = split_args(*args) end # @api private @@ -151,7 +151,7 @@ def invalid_kw_args @signature.invalid_kw_args_from(kw_args) end - def split_args(args) + def split_args(*args) kw_args = if @signature.has_kw_args_in?(args) args.pop.keys else diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index fb33d93a7..e4104ac78 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -117,6 +117,12 @@ def arity_kw(x, y:1, z:2); end it 'does not blow up when given a BasicObject as the last arg' do expect(valid?(BasicObject.new)).to eq(true) end + + it 'does not mutate the provided args array' do + args = [nil, { :y => 1 }] + described_class.new(test_method, args).valid? + expect(args).to eq([nil, { :y => 1 }]) + end end end From c313778d545e6ebb551b6331fec4d934e45cb8a9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 10:22:34 -0800 Subject: [PATCH 0460/1179] Replace multiple conditionals with a single case statement. --- lib/rspec/mocks/method_signature_verifier.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 562e87993..2f77192ae 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -15,15 +15,11 @@ def initialize(method) end def non_kw_args_error - if min_non_kw_args == max_non_kw_args - return min_non_kw_args.to_s + case max_non_kw_args + when min_non_kw_args then min_non_kw_args.to_s + when INFINITY then "#{min_non_kw_args} or more" + else "#{min_non_kw_args} to #{max_non_kw_args}" end - - if max_non_kw_args == INFINITY - return "#{min_non_kw_args} or more" - end - - "#{min_non_kw_args} to #{max_non_kw_args}" end if RubyFeatures.optional_and_splat_args_supported? From 0ccef42b74af7ef9e57381ff02b2453122d1f283 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 10:26:44 -0800 Subject: [PATCH 0461/1179] `error` => `error_message` It's not a ruby error object, it's just the message, so the new name is more accurate. --- lib/rspec/mocks/error_generator.rb | 2 +- lib/rspec/mocks/method_signature_verifier.rb | 2 +- lib/rspec/mocks/verifying_proxy.rb | 2 +- spec/rspec/mocks/method_signature_verifier_spec.rb | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index cd368ffef..17d560415 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -82,7 +82,7 @@ def raise_non_public_error(method_name, visibility) # @private def raise_arity_error(verifier) - __raise verifier.error + __raise verifier.error_message end # @private diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 2f77192ae..989d45b21 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -115,7 +115,7 @@ def valid? end # @api private - def error + def error_message if missing_kw_args.any? "Missing required keyword arguments: %s" % [ missing_kw_args.join(", ") diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index c736db0c0..1dee2e6d7 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -128,7 +128,7 @@ def ensure_arity!(actual_args) @method_reference.when_defined do |method| verifier = MethodSignatureVerifier.new(method, actual_args) unless verifier.valid? - raise ArgumentError, verifier.error + raise ArgumentError, verifier.error_message end end end diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index e4104ac78..95c6175d5 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -15,11 +15,11 @@ def valid?(*args) end def description - described_class.new(test_method, []).error[/Expected (.*),/, 1] + described_class.new(test_method, []).error_message[/Expected (.*),/, 1] end def error_for(*args) - described_class.new(test_method, args).error + described_class.new(test_method, args).error_message end describe 'with a method with arguments' do From ae521dbcbe91eaf8798f45a56d52c009a1888147 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 10:32:18 -0800 Subject: [PATCH 0462/1179] Update method names, etc to reflect our check is more than arity. Arity is just the number of arguments but we check more than that now, so it's good to reflect that. --- features/verifying_doubles/partial_doubles.feature | 4 ++-- lib/rspec/mocks/configuration.rb | 2 +- lib/rspec/mocks/error_generator.rb | 2 +- lib/rspec/mocks/verifying_message_expecation.rb | 8 ++++---- lib/rspec/mocks/verifying_proxy.rb | 10 +++++----- spec/rspec/mocks/verifying_message_expecation_spec.rb | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/features/verifying_doubles/partial_doubles.feature b/features/verifying_doubles/partial_doubles.feature index c4387031f..687f398d6 100644 --- a/features/verifying_doubles/partial_doubles.feature +++ b/features/verifying_doubles/partial_doubles.feature @@ -1,7 +1,7 @@ Feature: Partial doubles - When the `verify_partial_doubles` configuration option is set, the same arity - and method existince checks that are performed for `object_double` are also + When the `verify_partial_doubles` configuration option is set, the same argument + and method existence checks that are performed for `object_double` are also performed on partial doubles. You should set this unless you have a good reason not to. It defaults to off only for backwards compatibility. diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 2897d293b..ef604d8b3 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -108,7 +108,7 @@ def transfer_nested_constants=(val) end # When set to true, partial mocks will be verified the same as object - # doubles. Any stubs will have their arity checked against the original + # doubles. Any stubs will have their arguments checked against the original # method, and methods that do not exist cannot be stubbed. def verify_partial_doubles=(val) @verify_partial_doubles = !!val diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 17d560415..0ea80650f 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -81,7 +81,7 @@ def raise_non_public_error(method_name, visibility) end # @private - def raise_arity_error(verifier) + def raise_invalid_arguments_error(verifier) __raise verifier.error_message end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 603f007c2..75417527c 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -5,7 +5,7 @@ module Mocks # A message expectation that knows about the real implementation of the # message being expected, so that it can verify that any expectations - # have the correct arity. + # have the valid arguments. # @api private class VerifyingMessageExpectation < MessageExpectation @@ -35,14 +35,14 @@ def with(*args, &block) super end - ensure_arity!(expected_args) + validate_arguments!(expected_args) end super end private - def ensure_arity!(actual_args) + def validate_arguments!(actual_args) return if method_reference.nil? method_reference.when_defined do |method| @@ -51,7 +51,7 @@ def ensure_arity!(actual_args) # Fail fast is required, otherwise the message expecation will fail # as well ("expected method not called") and clobber this one. @failed_fast = true - @error_generator.raise_arity_error(verifier) + @error_generator.raise_invalid_arguments_error(verifier) end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 1dee2e6d7..98034324d 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -43,7 +43,7 @@ def ensure_publicly_implemented(method_name, object) # A verifying proxy mostly acts like a normal proxy, except that it # contains extra logic to try and determine the validity of any expectation # set on it. This includes whether or not methods have been defined and the - # arity of method calls. + # validatiy of arguments on method calls. # # In all other ways this behaves like a normal proxy. It only adds the # verification behaviour to specific methods then delegates to the parent @@ -64,7 +64,7 @@ def initialize(object, order_group, name, method_reference_class) @method_reference_class = method_reference_class # A custom method double is required to pass through a way to lookup - # methods to determine their arity. This is only relevant if the doubled + # methods to determine their parameters. This is only relevant if the doubled # class is loaded. @method_doubles = Hash.new do |h, k| h[k] = VerifyingMethodDouble.new(@object, k, self, method_reference[k]) @@ -91,7 +91,7 @@ def initialize(object, expectation_ordering) @doubled_module = DirectObjectReference.new(object) # A custom method double is required to pass through a way to lookup - # methods to determine their arity. + # methods to determine their parameters. @method_doubles = Hash.new do |h, k| h[k] = VerifyingExistingMethodDouble.new(object, k, self) end @@ -118,13 +118,13 @@ def add_expectation(*arg) end def proxy_method_invoked(obj, *args, &block) - ensure_arity!(args) + validate_arguments!(args) super end private - def ensure_arity!(actual_args) + def validate_arguments!(actual_args) @method_reference.when_defined do |method| verifier = MethodSignatureVerifier.new(method, actual_args) unless verifier.valid? diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index a5060e3b3..2fae9812b 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -23,7 +23,7 @@ module Mocks it 'raises error' do args = ["abc123", "xyz987"] subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) - expect(error_generator).to receive(:raise_arity_error). + expect(error_generator).to receive(:raise_invalid_arguments_error). with(instance_of(MethodSignatureVerifier)) subject.with(*args) @@ -33,7 +33,7 @@ module Mocks describe 'when called with arguments' do it 'matches arity against the number of arguments' do subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) - expect(error_generator).not_to receive(:raise_arity_error) + expect(error_generator).not_to receive(:raise_invalid_arguments_error) subject.with("abc123") end @@ -49,7 +49,7 @@ module Mocks describe 'when called with no arguments matcher' do it 'matches arity to 0' do subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) - expect(error_generator).to receive(:raise_arity_error). + expect(error_generator).to receive(:raise_invalid_arguments_error). with(instance_of(MethodSignatureVerifier)) subject.with(no_args) From 55a76e053b9ecab8bcf55bf8118c83330116cb43 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 18:20:26 -0800 Subject: [PATCH 0463/1179] Rename method. It's a description of the arity so let's call it that. --- lib/rspec/mocks/method_signature_verifier.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 989d45b21..93b8b0ed1 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -14,7 +14,7 @@ def initialize(method) classify_parameters end - def non_kw_args_error + def non_kw_args_arity_description case max_non_kw_args when min_non_kw_args then min_non_kw_args.to_s when INFINITY then "#{min_non_kw_args} or more" @@ -126,7 +126,7 @@ def error_message ] elsif !valid_non_kw_args? "Wrong number of arguments. Expected %s, got %s." % [ - @signature.non_kw_args_error, + @signature.non_kw_args_arity_description, non_kw_args.length ] end From fd3869c25dad6d4f87a7fd3edbdd55f08a8cbfde Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 17:59:25 -0800 Subject: [PATCH 0464/1179] Refactor: make caller pass a signature object. This clears the way to use a slightly different signature implementation (e.g. for blocks). --- lib/rspec/mocks/method_reference.rb | 4 ++-- lib/rspec/mocks/method_signature_verifier.rb | 4 ++-- lib/rspec/mocks/verifying_message_expecation.rb | 4 ++-- lib/rspec/mocks/verifying_proxy.rb | 8 ++++---- spec/rspec/mocks/method_signature_verifier_spec.rb | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index a1e18074f..85021ade3 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -43,9 +43,9 @@ def defined? end end - def when_defined + def with_signature if original = original_method - yield original + yield MethodSignature.new(original) end end diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 93b8b0ed1..b7139f3d1 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -102,8 +102,8 @@ class MethodSignatureVerifier # @api private attr_reader :non_kw_args, :kw_args - def initialize(method, args) - @signature = MethodSignature.new(method) + def initialize(signature, args) + @signature = signature @non_kw_args, @kw_args = split_args(*args) end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 75417527c..39f0254ab 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -45,8 +45,8 @@ def with(*args, &block) def validate_arguments!(actual_args) return if method_reference.nil? - method_reference.when_defined do |method| - verifier = MethodSignatureVerifier.new(method, actual_args) + method_reference.with_signature do |signature| + verifier = MethodSignatureVerifier.new(signature, actual_args) unless verifier.valid? # Fail fast is required, otherwise the message expecation will fail # as well ("expected method not called") and clobber this one. diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 98034324d..36d1ae00d 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -125,8 +125,8 @@ def proxy_method_invoked(obj, *args, &block) private def validate_arguments!(actual_args) - @method_reference.when_defined do |method| - verifier = MethodSignatureVerifier.new(method, actual_args) + @method_reference.with_signature do |signature| + verifier = MethodSignatureVerifier.new(signature, actual_args) unless verifier.valid? raise ArgumentError, verifier.error_message end @@ -152,8 +152,8 @@ def initialize(object, method_name, proxy) save_original_method! end - def when_defined - yield original_method + def with_signature + yield MethodSignature.new(original_method) end def unimplemented? diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index 95c6175d5..195162dbe 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -4,22 +4,22 @@ module RSpec module Mocks describe MethodSignatureVerifier do describe '#verify!' do - subject { described_class.new(test_method) } + let(:signature) { MethodSignature.new(test_method) } def valid_non_kw_args?(arity) - described_class.new(test_method, [nil] * arity).valid? + described_class.new(signature, [nil] * arity).valid? end def valid?(*args) - described_class.new(test_method, args).valid? + described_class.new(signature, args).valid? end def description - described_class.new(test_method, []).error_message[/Expected (.*),/, 1] + described_class.new(signature, []).error_message[/Expected (.*),/, 1] end def error_for(*args) - described_class.new(test_method, args).error_message + described_class.new(signature, args).error_message end describe 'with a method with arguments' do @@ -120,7 +120,7 @@ def arity_kw(x, y:1, z:2); end it 'does not mutate the provided args array' do args = [nil, { :y => 1 }] - described_class.new(test_method, args).valid? + described_class.new(signature, args).valid? expect(args).to eq([nil, { :y => 1 }]) end end From 2f3328662d85e704f7d0a5b94c15b61f25ae81a7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 23:24:02 -0800 Subject: [PATCH 0465/1179] Reword docstrings. I'm about to introduce a new `description` method and having docstrings say "it is described precisely" when not referring to the description is confusing. --- .../mocks/method_signature_verifier_spec.rb | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index 195162dbe..c43e36bfa 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -14,7 +14,7 @@ def valid?(*args) described_class.new(signature, args).valid? end - def description + def error_description described_class.new(signature, []).error_message[/Expected (.*),/, 1] end @@ -37,8 +37,8 @@ def arity_two(x, y); end expect(valid?(1, {})).to eq(true) end - it 'is described precisely' do - expect(description).to eq("2") + it 'describes the arity precisely' do + expect(error_description).to eq("2") end end @@ -54,8 +54,8 @@ def arity_splat(_, *); end expect(valid_non_kw_args?(3)).to eq(true) end - it 'is described with no upper bound' do - expect(description).to eq("1 or more") + it 'describes the arity with no upper bound' do + expect(error_description).to eq("1 or more") end end @@ -77,12 +77,12 @@ def arity_optional(x, y, z = 1); end end if optional_and_splat_args_supported? - it 'is described as a range' do - expect(description).to eq("2 to 3") + it 'describes the arity as a range' do + expect(error_description).to eq("2 to 3") end else - it 'is described with no upper bound' do - expect(description).to eq("2 or more") + it 'describes the arity with no upper bound' do + expect(error_description).to eq("2 or more") end end end @@ -104,12 +104,12 @@ def arity_kw(x, y:1, z:2); end expect(valid?(nil, :a => 1)).to eq(false) end - it 'is described precisely' do + it 'mentions the invalid keyword args in the error' do expect(error_for(nil, :a => 0, :b => 1)).to \ eq("Invalid keyword arguments provided: a, b") end - it 'is described precisely when arity is wrong' do + it 'describes invalid arity precisely' do expect(error_for()).to \ eq("Wrong number of arguments. Expected 1, got 0.") end @@ -141,7 +141,7 @@ def arity_required_kw(x, y:, z:, a: 'default'); end expect(valid?(nil, nil)).to eq(false) end - it 'is described precisely' do + it 'mentions the missing required keyword args in the error' do expect(error_for(nil, :a => 0)).to \ eq("Missing required keyword arguments: y, z") end @@ -167,7 +167,7 @@ def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end expect(valid?).to eq(false) end - it 'is described precisely' do + it 'mentions missing required keyword args in the error' do expect(error_for(nil, :y => 1)).to \ eq("Missing required keyword arguments: z") end @@ -185,7 +185,7 @@ def arity_kw_arg_splat(x:, **rest); end expect(valid?(:x => 1, :y => 2)).to eq(true) end - it 'is described precisely' do + it 'mentions missing required keyword args in the error' do expect(error_for(:y => 1)).to \ eq("Missing required keyword arguments: x") end @@ -209,7 +209,7 @@ def arity_kw_arg_splat(x, **rest); end expect(valid?(nil, nil, :x => 1)).to eq(false) end - it 'is described precisely' do + it 'describes the arity precisely' do expect(error_for()).to \ eq("Wrong number of arguments. Expected 1, got 0.") end @@ -226,8 +226,8 @@ def arity_block(_, &block); end expect(valid_non_kw_args?(2)).to eq(false) end - it 'is described precisely' do - expect(description).to eq("1") + it 'describes the arity precisely' do + expect(error_description).to eq("1") end end end From e1aaad5c464a6d80b07bf98b4b0a95791b8c91ce Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Jan 2014 18:03:41 -0800 Subject: [PATCH 0466/1179] Use method signature verifier for `and_yield` block verification. --- lib/rspec/mocks/error_generator.rb | 4 +- lib/rspec/mocks/message_expectation.rb | 7 ++- lib/rspec/mocks/method_signature_verifier.rb | 52 +++++++++++++++++-- spec/rspec/mocks/double_spec.rb | 10 ++++ .../mocks/method_signature_verifier_spec.rb | 36 +++++++++++++ 5 files changed, 102 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 0ea80650f..88b1ea574 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -143,8 +143,8 @@ def raise_missing_block_error(args_to_yield) end # @private - def raise_wrong_arity_error(args_to_yield, arity) - __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with arity of #{arity}" + def raise_wrong_arity_error(args_to_yield, signature) + __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with #{signature.description}" end # @private diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index e449d22b7..b2423f1eb 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -554,10 +554,13 @@ def call(*args_to_ignore, &block) @error_generator.raise_missing_block_error @args_to_yield unless block value = nil + block_signature = BlockSignature.new(block) + @args_to_yield.each do |args| - if block.arity > -1 && args.length != block.arity - @error_generator.raise_wrong_arity_error args, block.arity + unless MethodSignatureVerifier.new(block_signature, args).valid? + @error_generator.raise_wrong_arity_error(args, block_signature) end + value = @eval_context ? @eval_context.instance_exec(*args, &block) : block.call(*args) end value diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index b7139f3d1..d457153c8 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -23,6 +23,30 @@ def non_kw_args_arity_description end if RubyFeatures.optional_and_splat_args_supported? + def description + @description ||= begin + parts = [] + + unless non_kw_args_arity_description == "0" + parts << "arity of #{non_kw_args_arity_description}" + end + + if @optional_kw_args.any? + parts << "optional keyword args (#{@optional_kw_args.map(&:inspect).join(", ")})" + end + + if @required_kw_args.any? + parts << "required keyword args (#{@required_kw_args.map(&:inspect).join(", ")})" + end + + if @allows_any_kw_args + parts << "any additional keyword args" + end + + parts.join(" and ") + end + end + def missing_kw_args_from(given_kw_args) @required_kw_args - given_kw_args end @@ -41,7 +65,7 @@ def has_kw_args_in?(args) def classify_parameters optional_non_kw_args = @min_non_kw_args = 0 - optional_kw_args, @required_kw_args = [], [] + @optional_kw_args, @required_kw_args = [], [] @allows_any_kw_args = false @method.parameters.each do |(type, name)| @@ -49,7 +73,7 @@ def classify_parameters # def foo(a:) when :keyreq then @required_kw_args << name # def foo(a: 1) - when :key then optional_kw_args << name + when :key then @optional_kw_args << name # def foo(**kw_args) when :keyrest then @allows_any_kw_args = true # def foo(a) @@ -62,9 +86,13 @@ def classify_parameters end @max_non_kw_args = @min_non_kw_args + optional_non_kw_args - @allowed_kw_args = @required_kw_args + optional_kw_args + @allowed_kw_args = @required_kw_args + @optional_kw_args end else + def description + "arity of #{non_kw_args_arity_description}" + end + def missing_kw_args_from(given_kw_args) [] end @@ -94,6 +122,24 @@ def classify_parameters INFINITY = 1/0.0 end + # Deals with the slightly different semantics of block arguments. + # For methods, arguments are required unless a default value is provided. + # For blocks, arguments are optional, even if no default value is provided. + # + # However, we want to treat block args as required since you virtually always + # want to pass a value for each received argument and our `and_yield` has + # treated block args as required for many years. + # + # @api private + class BlockSignature < MethodSignature + if RubyFeatures.optional_and_splat_args_supported? + def classify_parameters + super + @min_non_kw_args = @max_non_kw_args unless @max_non_kw_args == INFINITY + end + end + end + # Figures out wheter a given method can accept various arguments. # Surprisingly non-trivial. # diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 8b62a6913..2d5da3374 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -576,6 +576,16 @@ def initialize(amount, units) }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" yielded |\"wha\", \"zup\"| to block with arity of 1") end + if kw_args_supported? + it 'fails when calling yielding method with invalid kw args' do + @double.should_receive(:yield_back).and_yield(:x => 1, :y => 2) + expect { + eval("@double.yield_back { |x: 1| }") + }.to raise_error(RSpec::Mocks::MockExpectationError, + 'Double "test double" yielded |{:x=>1, :y=>2}| to block with optional keyword args (:x)') + end + end + it "fails when calling yielding method consecutively with wrong arity" do @double.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). and_yield('down'). diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index c43e36bfa..fc22cda01 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -22,6 +22,10 @@ def error_for(*args) described_class.new(signature, args).error_message end + def signature_description + signature.description + end + describe 'with a method with arguments' do def arity_two(x, y); end @@ -40,6 +44,10 @@ def arity_two(x, y); end it 'describes the arity precisely' do expect(error_description).to eq("2") end + + it 'mentions only the arity in the description' do + expect(signature_description).to eq("arity of 2") + end end describe 'a method with splat arguments' do @@ -57,6 +65,10 @@ def arity_splat(_, *); end it 'describes the arity with no upper bound' do expect(error_description).to eq("1 or more") end + + it 'mentions only the arity in the description' do + expect(signature_description).to eq("arity of 1 or more") + end end describe 'a method with optional arguments' do @@ -123,6 +135,10 @@ def arity_kw(x, y:1, z:2); end described_class.new(signature, args).valid? expect(args).to eq([nil, { :y => 1 }]) end + + it 'mentions the arity and optional kw args in the description' do + expect(signature_description).to eq("arity of 1 and optional keyword args (:y, :z)") + end end end @@ -150,6 +166,11 @@ def arity_required_kw(x, y:, z:, a: 'default'); end expect(error_for(nil, nil, :z => 0, :y => 1)).to \ eq("Wrong number of arguments. Expected 1, got 2.") end + + it 'mentions the arity, optional kw args and required kw args in the description' do + expect(signature_description).to \ + eq("arity of 1 and optional keyword args (:a) and required keyword args (:y, :z)") + end end describe 'a method with required keyword arguments and a splat' do @@ -171,6 +192,11 @@ def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end expect(error_for(nil, :y => 1)).to \ eq("Missing required keyword arguments: z") end + + it 'mentions the arity, optional kw args and required kw args in the description' do + expect(signature_description).to \ + eq("arity of 1 or more and optional keyword args (:a) and required keyword args (:y, :z)") + end end describe 'a method with required keyword arguments and a keyword arg splat' do @@ -189,6 +215,11 @@ def arity_kw_arg_splat(x:, **rest); end expect(error_for(:y => 1)).to \ eq("Missing required keyword arguments: x") end + + it 'mentions the required kw args and keyword splat in the description' do + expect(signature_description).to \ + eq("required keyword args (:x) and any additional keyword args") + end end describe 'a method with a required arg and a keyword arg splat' do @@ -213,6 +244,11 @@ def arity_kw_arg_splat(x, **rest); end expect(error_for()).to \ eq("Wrong number of arguments. Expected 1, got 0.") end + + it 'mentions the required kw args and keyword splat in the description' do + expect(signature_description).to \ + eq("arity of 1 and any additional keyword args") + end end end From 08169269c3e60408afc6d9cd22858a39f833c640 Mon Sep 17 00:00:00 2001 From: Mauricio Linhares Date: Thu, 30 Jan 2014 23:08:20 -0300 Subject: [PATCH 0467/1179] RSpec::Mocks::NamedObjectReference doesn't have a name method, creating specific spec for this case so there are no false positives --- lib/rspec/mocks/error_generator.rb | 2 ++ lib/rspec/mocks/example_methods.rb | 4 ++-- spec/rspec/mocks/verifying_double_spec.rb | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index cd368ffef..79d977720 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -18,6 +18,8 @@ module Mocks # @private NegationUnsupportedError = Class.new(StandardError) + VerifyingDoubleNotDefinedError = Class.new(StandardError) + # @private class ErrorGenerator attr_writer :opts diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 4448be4a8..7b8121652 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -178,8 +178,8 @@ def self.declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? - raise NameError, - "#{ref.name} is not a defined constant. " + + raise VerifyingDoubleNotDefinedError, + "#{ref.description} is not a defined constant. " + "Perhaps you misspelt it? " + "Disable check with verify_doubled_constant_names configuration option." end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index b9156f329..dd6c0d3d9 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -535,13 +535,13 @@ module Mocks it 'prevents creation of instance doubles for unloaded constants' do expect { instance_double('LoadedClas') - }.to raise_error(NameError) + }.to raise_error(VerifyingDoubleNotDefinedError) end it 'prevents creation of class doubles for unloaded constants' do expect { class_double('LoadedClas') - }.to raise_error(NameError) + }.to raise_error(VerifyingDoubleNotDefinedError) end end From 745d2a678108a726066515d1e907313382113861 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 31 Jan 2014 14:26:39 +1100 Subject: [PATCH 0468/1179] changelog for #550 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 40c6829cc..a3ee453cb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -45,6 +45,8 @@ Bug Fixes: methods. (Sam Phippen) * Correct stub of undefined parent modules all the way down when stubbing a nested constant. (Xavier Shay) +* Raise `VerifyingDoubleNotDefinedError` when a constant is not defined for + a verifying class double. (Maurício Linhares) Enhancements: From e8d6911b762804ed87863847dfcd0521c05fd3ea Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 31 Jan 2014 10:05:41 -0800 Subject: [PATCH 0469/1179] Add regression spec for #549. This is already fixed in 3.0 but it's good to add a spec to prevent future regressions. --- spec/rspec/mocks/block_return_value_spec.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 6d69aeb34..cf797fbda 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -23,6 +23,17 @@ obj.stub(:foo) { 'bar' } expect(obj.foo).to eq('bar') end + + # The "receives a block" part is important: 1.8.7 has a bug that reports the + # wrong arity when a block receives a block. + it 'forwards all given args to the block, even when it receives a block' do + obj = Object.new + yielded_args = [] + eval("obj.stub(:foo) { |*args, &bl| yielded_args << args }") + obj.foo(1, 2, 3) + + expect(yielded_args).to eq([[1, 2, 3]]) + end end describe "with" do From 7b9930ba946e288c095cf9ebd3ed27d3820edb65 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 1 Feb 2014 09:20:09 -0800 Subject: [PATCH 0470/1179] Update changelog with 2.14.5 release notes. [ci skip] --- Changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Changelog.md b/Changelog.md index a3ee453cb..446219bee 100644 --- a/Changelog.md +++ b/Changelog.md @@ -151,6 +151,16 @@ Enhancements: * Add a config option to yield the receiver to `any_instance` implementation blocks. (Sam Phippen) +### 2.14.5 / 2014-02-01 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.14.5) + +Bug Fixes: + +* Fix regression that caused block implementations to not receive all + args on 1.8.7 if the block also receives a block, due to Proc#arity + reporting `1` no matter how many args the block receives if it + receives a block, too. (Myron Marston) + ### 2.14.4 / 2013-10-15 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.3...v2.14.4) From 492c3ec19303f1cf50dbcb5142428835f6339aa2 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Fri, 7 Feb 2014 17:20:55 +0900 Subject: [PATCH 0471/1179] Remove support for `and_return { value }` --- Changelog.md | 1 + lib/rspec/mocks/message_expectation.rb | 32 ++++++------------- spec/rspec/mocks/and_return_spec.rb | 17 ++++++++++ spec/rspec/mocks/block_return_value_spec.rb | 2 +- ...bining_implementation_instructions_spec.rb | 9 ------ spec/rspec/mocks/double_spec.rb | 6 ---- spec/rspec/mocks/stub_spec.rb | 6 ---- 7 files changed, 28 insertions(+), 45 deletions(-) create mode 100644 spec/rspec/mocks/and_return_spec.rb diff --git a/Changelog.md b/Changelog.md index 446219bee..bc6574138 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,7 @@ Breaking Changes for 3.0.0: get reset, to help surface issues when you accidentally retain references to test doubles and attempt to reuse them in another example. (Myron Marston) +* Remove support for `and_return { value }`. (Yuji Nakayama) Bug Fixes: diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index b2423f1eb..990519225 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -77,7 +77,6 @@ def expected_args # @overload and_return(value) # @overload and_return(first_value, second_value) - # @overload and_return(&block) # # Tells the object to return a value when it receives the message. Given # more than one value, the first value is returned the first time the @@ -87,9 +86,6 @@ def expected_args # If the message is received more times than there are values, the last # value is received for every subsequent call. # - # The block format is still supported, but is unofficially deprecated in - # favor of just passing a block to the stub method. - # # @example # # allow(counter).to receive(:count).and_return(1) @@ -103,29 +99,19 @@ def expected_args # counter.count # => 3 # counter.count # => 3 # # etc - # - # # Supported, but ... - # allow(counter).to receive(:count).and_return { 1 } - # counter.count # => 1 - # - # # ... this is prefered - # allow(counter).to receive(:count) { 1 } - # counter.count # => 1 - def and_return(*values, &implementation) + def and_return(*values) if negative? raise "`and_return` is not supported with negative message expectations" - else - @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least) - - if implementation - # TODO: deprecate `and_return { value }` - self.inner_implementation_action = implementation - else - self.terminal_implementation_action = AndReturnImplementation.new(values) - end + end - nil + if block_given? + raise ArgumentError, "Implementation blocks aren't supported with `and_return`" end + + @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least) + self.terminal_implementation_action = AndReturnImplementation.new(values) + + nil end def and_yield_receiver_to_implementation diff --git a/spec/rspec/mocks/and_return_spec.rb b/spec/rspec/mocks/and_return_spec.rb new file mode 100644 index 000000000..8a2ccb282 --- /dev/null +++ b/spec/rspec/mocks/and_return_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +module RSpec + module Mocks + describe 'and_return' do + let(:obj) { double('obj') } + + context 'when a block is passed' do + it 'raises ArgumentError' do + expect { + obj.stub(:foo).and_return('bar') { 'baz' } + }.to raise_error(ArgumentError, /implementation block/i) + end + end + end + end +end diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index cf797fbda..540d67f45 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -50,7 +50,7 @@ end end - %w[once twice ordered and_return].each do |method| + %w[once twice ordered].each do |method| describe method do it "returns the value of executing the block" do obj = Object.new diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index e58e60003..0c72dde08 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -84,15 +84,6 @@ def verify_combined_implementation end end - it 'can combine and_yield and and_return with a block' do - dbl = double - dbl.stub(:foo).and_yield(5).and_return { :return } - - expect { |b| - expect(dbl.foo(&b)).to eq(:return) - }.to yield_with_args(5) - end - it 'can combine and_yield and and_raise' do dbl = double dbl.stub(:foo).and_yield(5).and_raise("boom") diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 2d5da3374..86cd877a4 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -235,12 +235,6 @@ module Mocks verify @double end - it "allows block to calculate return values" do - @double.should_receive(:something).with("a","b","c").and_return { |a,b,c| c+b+a } - expect(@double.something("a","b","c")).to eq "cba" - verify @double - end - it "allows parameter as return value" do @double.should_receive(:something).with("a","b","c").and_return("booh") expect(@double.something("a","b","c")).to eq "booh" diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 4174e959b..5caeb84ec 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -263,12 +263,6 @@ def non_prepended_method @stub.foo("bar") @stub.foo("baz") end - - it "calculates return value by executing block passed to #and_return" do - @stub.stub(:something).with("a","b","c").and_return { |a,b,c| c+b+a } - expect(@stub.something("a","b","c")).to eq "cba" - verify @stub - end end describe "A method stub with args" do From e1c9a3e21435b2159fcdbcf1e6aebd1554d2f890 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Fri, 7 Feb 2014 17:25:57 +0900 Subject: [PATCH 0472/1179] Disallow `and_return` without arguments --- Changelog.md | 2 +- lib/rspec/mocks/message_expectation.rb | 3 ++- spec/rspec/mocks/and_return_spec.rb | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index bc6574138..10ce58c90 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,7 +16,7 @@ Breaking Changes for 3.0.0: get reset, to help surface issues when you accidentally retain references to test doubles and attempt to reuse them in another example. (Myron Marston) -* Remove support for `and_return { value }`. (Yuji Nakayama) +* Remove support for `and_return { value }` and `and_return` without arguments. (Yuji Nakayama) Bug Fixes: diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 990519225..dd97a65b2 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -99,7 +99,7 @@ def expected_args # counter.count # => 3 # counter.count # => 3 # # etc - def and_return(*values) + def and_return(first_value, *values) if negative? raise "`and_return` is not supported with negative message expectations" end @@ -108,6 +108,7 @@ def and_return(*values) raise ArgumentError, "Implementation blocks aren't supported with `and_return`" end + values.unshift(first_value) @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least) self.terminal_implementation_action = AndReturnImplementation.new(values) diff --git a/spec/rspec/mocks/and_return_spec.rb b/spec/rspec/mocks/and_return_spec.rb index 8a2ccb282..7c5c8dbda 100644 --- a/spec/rspec/mocks/and_return_spec.rb +++ b/spec/rspec/mocks/and_return_spec.rb @@ -12,6 +12,12 @@ module Mocks }.to raise_error(ArgumentError, /implementation block/i) end end + + context 'when no argument is passed' do + it 'raises ArgumentError' do + expect { obj.stub(:foo).and_return }.to raise_error(ArgumentError) + end + end end end end From 3ed7151fb04377be84b875e3635c2b7c7299d640 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 8 Feb 2014 14:27:46 +1100 Subject: [PATCH 0473/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 5 +++-- script/clone_all_rspec_repos | 7 +++++-- script/functions.sh | 2 +- script/run_build | 7 +++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index b5970932f..420502e05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-01-25T09:32:04-08:00 from the rspec-dev repo. +# This file was generated on 2014-02-08T14:27:25+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: "script/clone_all_rspec_repos" @@ -13,11 +13,12 @@ rvm: - ruby-head - ree - jruby-18mode - - jruby-19mode + - jruby - jruby-head - rbx matrix: allow_failures: + - rvm: jruby-18mode - rvm: jruby-head - rvm: ruby-head fast_finish: true diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 12ecda828..35f5e6b9e 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-01-25T09:32:04-08:00 from the rspec-dev repo. +# This file was generated on 2014-02-08T14:27:25+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -12,7 +12,10 @@ if is_mri; then clone_repo "rspec-core" clone_repo "rspec-expectations" clone_repo "rspec-mocks" - clone_repo "rspec-support" + + if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ]; then + clone_repo "rspec-support" + fi popd else diff --git a/script/functions.sh b/script/functions.sh index 3855d648c..36282eb82 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-01-25T09:32:04-08:00 from the rspec-dev repo. +# This file was generated on 2014-02-08T14:27:25+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index 3fbf66dff..2a5c05557 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-01-25T09:32:04-08:00 from the rspec-dev repo. +# This file was generated on 2014-02-08T14:27:25+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -13,7 +13,10 @@ if is_mri; then run_spec_suite_for "rspec-core" run_spec_suite_for "rspec-expectations" run_spec_suite_for "rspec-mocks" - run_spec_suite_for "rspec-support" + + if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ]; then + run_spec_suite_for "rspec-support" + fi else echo "Skipping the rest of the build on non-MRI rubies" fi From 4b1a4b6e1cc8b9ce5345786cf73642c36b8edf08 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 10 Feb 2014 09:51:07 -0800 Subject: [PATCH 0474/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 3 ++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 420502e05..d460cc364 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-02-08T14:27:25+11:00 from the rspec-dev repo. +# This file was generated on 2014-02-10T09:50:59-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: "script/clone_all_rspec_repos" @@ -21,4 +21,5 @@ matrix: - rvm: jruby-18mode - rvm: jruby-head - rvm: ruby-head + - rvm: rbx fast_finish: true diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 35f5e6b9e..817d8eec7 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-02-08T14:27:25+11:00 from the rspec-dev repo. +# This file was generated on 2014-02-10T09:50:59-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 36282eb82..d9791e526 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-02-08T14:27:25+11:00 from the rspec-dev repo. +# This file was generated on 2014-02-10T09:50:59-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index 2a5c05557..a93c0c57f 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-02-08T14:27:25+11:00 from the rspec-dev repo. +# This file was generated on 2014-02-10T09:50:59-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From 39acf194e504346083dcfeee81c6eb30a267e294 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 10 Feb 2014 10:29:42 -0800 Subject: [PATCH 0475/1179] Use skip instead of pending. The semantics of pending have changed in rspec-core. --- spec/rspec/mocks/any_instance_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 19a4017e5..b6b32d5d5 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -968,7 +968,7 @@ def call(*args) context 'when used in conjunction with a `dup`' do it "doesn't cause an infinite loop" do - pending "This intermittently fails on JRuby" if RUBY_PLATFORM == 'java' + skip "This intermittently fails on JRuby" if RUBY_PLATFORM == 'java' Object.any_instance.stub(:some_method) o = Object.new From c199f49e904193d656f9d896ad82722431bcd22c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 12 Feb 2014 08:24:27 +1100 Subject: [PATCH 0476/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 3 +-- script/clone_all_rspec_repos | 4 ++-- script/functions.sh | 11 ++++++++++- script/run_build | 4 ++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index d460cc364..973f30b15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-02-10T09:50:59-08:00 from the rspec-dev repo. +# This file was generated on 2014-02-12T08:24:06+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: "script/clone_all_rspec_repos" @@ -18,7 +18,6 @@ rvm: - rbx matrix: allow_failures: - - rvm: jruby-18mode - rvm: jruby-head - rvm: ruby-head - rvm: rbx diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 817d8eec7..c54006df7 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-02-10T09:50:59-08:00 from the rspec-dev repo. +# This file was generated on 2014-02-12T08:24:06+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -13,7 +13,7 @@ if is_mri; then clone_repo "rspec-expectations" clone_repo "rspec-mocks" - if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ]; then + if rspec_support_compatible; then clone_repo "rspec-support" fi diff --git a/script/functions.sh b/script/functions.sh index d9791e526..761651f75 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-02-10T09:50:59-08:00 from the rspec-dev repo. +# This file was generated on 2014-02-12T08:24:06+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html @@ -52,6 +52,15 @@ function is_mri_192 { fi } +function rspec_support_compatible { + if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then + return 0 + else + return 1 + fi +} + + function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there travis_retry git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH diff --git a/script/run_build b/script/run_build index a93c0c57f..3befcf851 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-02-10T09:50:59-08:00 from the rspec-dev repo. +# This file was generated on 2014-02-12T08:24:06+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -14,7 +14,7 @@ if is_mri; then run_spec_suite_for "rspec-expectations" run_spec_suite_for "rspec-mocks" - if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ]; then + if rspec_support_compatible; then run_spec_suite_for "rspec-support" fi else From 80c740e5d7cd1959713cc13a84cc4f6c29155f2f Mon Sep 17 00:00:00 2001 From: Mauricio Linhares Date: Wed, 12 Feb 2014 01:27:19 -0300 Subject: [PATCH 0477/1179] Removes #to_str implememtation from test_double Having test_double implement to_str makes the Ruby runtime coerce it to a string whenever needed and this isn't the intended behavior for this object. This behavior led to issue #567 as Ruby will try to coerce the exception object into string when raising (here https://github.com/ruby/ruby/blob/191b373d26a48058bcc8955bf3afe426f60eaaea/eval.c#L678 ) an exception and since test_double does implement to_str it succeded and raises that instead of failing with the correct 'this is not an exception object' error. This fixes #567 by providing a better error message in it's case. --- lib/rspec/mocks/test_double.rb | 2 +- spec/rspec/mocks/double_spec.rb | 8 +++++--- spec/rspec/mocks/verifying_double_spec.rb | 8 ++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index a2c2b00e6..fce740681 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -53,7 +53,7 @@ def to_s inspect.gsub('<','[').gsub('>',']') end - alias_method :to_str, :to_s + #alias_method :to_str, :to_s # @private def respond_to?(message, incl_private=false) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 86cd877a4..2de3dfbb7 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -773,10 +773,12 @@ def add_call end end - describe "string representation generated by #to_str" do - it "looks the same as #to_s" do + describe "#to_str" do + it "should not respond to #to_str to avoid being coerced to strings by the runtime" do double = double("Foo") - expect(double.to_str).to eq double.to_s + expect { double.to_str }.to raise_error( + RSpec::Mocks::MockExpectationError, + 'Double "Foo" received unexpected message :to_str with (no args)') end end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 0876702ab..c32372202 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -466,6 +466,14 @@ module Mocks class_double(Object.new) }.to raise_error(/Module or String expected/) end + + it "trying to raise a class_double raises a TypeError" do + subject = Object.new + class_double("StubbedError").as_stubbed_const + allow(subject).to receive(:some_method).and_raise(StubbedError) + expect { subject.some_method }.to raise_error(TypeError, 'exception class/object expected') + end + end describe 'object doubles' do From 6c21ac668e19deaf54f12dae14fa4f658ec5f7fd Mon Sep 17 00:00:00 2001 From: Mauricio Linhares Date: Wed, 12 Feb 2014 09:44:20 -0300 Subject: [PATCH 0478/1179] Ignoring `raise double()` specs for ruby 1.9.2 This ignores specs that check for the removal of `to_str` from Ruby 1.9.2 as they won't work there due to the Ruby runtime calling `to_str` on a double without checking if the double implements `to_str`. --- lib/rspec/mocks/test_double.rb | 2 -- spec/rspec/mocks/double_spec.rb | 2 +- spec/rspec/mocks/verifying_double_spec.rb | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index fce740681..f77dc4028 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -53,8 +53,6 @@ def to_s inspect.gsub('<','[').gsub('>',']') end - #alias_method :to_str, :to_s - # @private def respond_to?(message, incl_private=false) __mock_proxy.null_object? ? true : super diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 2de3dfbb7..b00bea824 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -773,7 +773,7 @@ def add_call end end - describe "#to_str" do + describe "#to_str", :unless => RUBY_VERSION == '1.9.2' do it "should not respond to #to_str to avoid being coerced to strings by the runtime" do double = double("Foo") expect { double.to_str }.to raise_error( diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index c32372202..553e12620 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -467,7 +467,7 @@ module Mocks }.to raise_error(/Module or String expected/) end - it "trying to raise a class_double raises a TypeError" do + it "trying to raise a class_double raises a TypeError", :unless => RUBY_VERSION == '1.9.2' do subject = Object.new class_double("StubbedError").as_stubbed_const allow(subject).to receive(:some_method).and_raise(StubbedError) From 55c00d4e93d6e18fa47ea3d8c17f5c50261dae7b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 17 Feb 2014 08:59:57 -0800 Subject: [PATCH 0479/1179] Move enhancements above bug fixes in changelog. [ci skip] --- Changelog.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Changelog.md b/Changelog.md index 10ce58c90..816ebc428 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,20 @@ Breaking Changes for 3.0.0: example. (Myron Marston) * Remove support for `and_return { value }` and `and_return` without arguments. (Yuji Nakayama) +Enhancements: + +* Add `receive_message_chain` which provides the functionality of the old + `stub_chain` for the new allow/expect syntax. Use it like so: `allow(...).to + receive_message_chain(:foo, :bar, :bazz)`. (Sam Phippen). +* Change argument matchers to use `===` as their primary matching + protocol, as their semantics mirror that of a case or rescue statement + (which uses `===` for matching). (Myron Marston) +* Add `RSpec::Mocks.with_temporary_scope`, which allows you to create + temporary rspec-mocks scopes in arbitrary places (such as a + `before(:all)` hook). (Myron Marston) +* Support keyword arguments when checking arity with verifying doubles. + (Xavier Shay) + Bug Fixes: * Fix regression in 3.0.0.beta1 that caused `double("string_name" => :value)` @@ -49,20 +63,6 @@ Bug Fixes: * Raise `VerifyingDoubleNotDefinedError` when a constant is not defined for a verifying class double. (Maurício Linhares) -Enhancements: - -* Add `receive_message_chain` which provides the functionality of the old - `stub_chain` for the new allow/expect syntax. Use it like so: `allow(...).to - receive_message_chain(:foo, :bar, :bazz)`. (Sam Phippen). -* Change argument matchers to use `===` as their primary matching - protocol, as their semantics mirror that of a case or rescue statement - (which uses `===` for matching). (Myron Marston) -* Add `RSpec::Mocks.with_temporary_scope`, which allows you to create - temporary rspec-mocks scopes in arbitrary places (such as a - `before(:all)` hook). (Myron Marston) -* Support keyword arguments when checking arity with verifying doubles. - (Xavier Shay) - ### 3.0.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) From 1c6c770df2c2ed305301eff925a3a28ca85fefdb Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 17 Feb 2014 14:19:17 -0800 Subject: [PATCH 0480/1179] Grammar in Changelog. [ci-skip] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 816ebc428..0add085cd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -24,7 +24,7 @@ Enhancements: `stub_chain` for the new allow/expect syntax. Use it like so: `allow(...).to receive_message_chain(:foo, :bar, :bazz)`. (Sam Phippen). * Change argument matchers to use `===` as their primary matching - protocol, as their semantics mirror that of a case or rescue statement + protocol, since their semantics mirror that of a case or rescue statement (which uses `===` for matching). (Myron Marston) * Add `RSpec::Mocks.with_temporary_scope`, which allows you to create temporary rspec-mocks scopes in arbitrary places (such as a From afe60ccbee2c12350b24f0a1b70086a2ec564448 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 17 Feb 2014 20:54:25 -0800 Subject: [PATCH 0481/1179] Add changelog entry for #577. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 0add085cd..484a696ad 100644 --- a/Changelog.md +++ b/Changelog.md @@ -62,6 +62,8 @@ Bug Fixes: nested constant. (Xavier Shay) * Raise `VerifyingDoubleNotDefinedError` when a constant is not defined for a verifying class double. (Maurício Linhares) +* Remove `Double#to_str`, which caused confusing `raise some_double` + behavior. (Maurício Linhares) ### 3.0.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) From 10cdaa3cdd9d40eb3f1ba94ad97fe21c200d0f7d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 17 Feb 2014 21:45:54 -0800 Subject: [PATCH 0482/1179] Add release notes from 2.99.0.beta2. [ci skip] --- Changelog.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Changelog.md b/Changelog.md index 484a696ad..74ac1cec7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -129,6 +129,21 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.0.beta2 / 2014-02-17 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v2.99.0.beta2) + +Deprecations: + +* Deprecate `RSpec::Mocks::Mock` in favor of `RSpec::Mocks::Double`. + (Myron Marston) +* Deprecate the `host` argument of `RSpec::Mocks.setup`. Instead + `RSpec::Mocks::ExampleMethods` should be included directly in the scope where + RSpec's mocking capabilities are used. (Sam Phippen) +* Deprecate using any of rspec-mocks' features outside the per-test + lifecycle (e.g. from a `before(:all)` hook). (Myron Marston) +* Deprecate re-using a test double in another example. (Myron Marston) +* Deprecate `and_return { value }` and `and_return` without arguments. (Yuji Nakayama) + ### 2.99.0.beta1 / 2013-11-07 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.99.0.beta1) From d40221e96f0a82564cf2e83887d0984fdacf3501 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 17 Feb 2014 21:56:01 -0800 Subject: [PATCH 0483/1179] Update changelog for 3.0.0.beta2 release. [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 74ac1cec7..874c63c30 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.0.0.beta2 Development -[full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...master) +### 3.0.0.beta2 / 2014-02-17 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) Breaking Changes for 3.0.0: From e5c2ba028e8568a32a61044aeceef26374587535 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 17 Feb 2014 21:58:02 -0800 Subject: [PATCH 0484/1179] Release 3.0.0.beta2 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 19b8f4a45..4d4da963d 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module Version - STRING = '3.0.0.beta1' + STRING = '3.0.0.beta2' end end end From 9343adebba63b708a8f074cbf1f49ff2870bc284 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 17 Feb 2014 22:53:46 -0800 Subject: [PATCH 0485/1179] Prepare changelog for 3.0.0.rc1 development. --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 874c63c30..86fe3a877 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,6 @@ +### 3.0.0.rc1 Development +[full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...master) + ### 3.0.0.beta2 / 2014-02-17 [full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) From 577c7c00ac9af49d1fad3c3c740951c83400d0d8 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Mon, 17 Feb 2014 16:47:16 +0000 Subject: [PATCH 0486/1179] Fix any instance mark_invoked blocks not being cleared properly. This issue was caused by the order that mocks were torn down in space.rb. This happened because (in order): 1. Each mock proxy is verified 2. Each any_instance recorder is verified any_instance verification also currently holds the code for tearing down any_instance recorders also. If any mock proxy through an exception whilst it was being verified then none of the any_instance recorders were verified (which makes sense as verification of mocks has already failed). This patch adds a teardown of the any_instance_recorders in reset_all to RSpec::Mocks::Space --- lib/rspec/mocks/space.rb | 2 ++ spec/rspec/mocks/any_instance_spec.rb | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index ec2c65ac0..3573613e0 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -62,6 +62,8 @@ def verify_all def reset_all proxies.each_value { |proxy| proxy.reset } @constant_mutators.reverse.each { |mut| mut.idempotently_reset } + any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! } + any_instance_recorders.clear end def register_constant_mutator(mutator) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index b6b32d5d5..d3be7cb08 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -1039,6 +1039,21 @@ def dup(funky_option) expect(instance.existing_method).to eq :existing_method_return_value end + + it "restores the original behaviour, even if the expectation fails" do + klass.any_instance.should_receive(:existing_method).with(1).and_return(:stubbed_return_value) + + instance = klass.new + begin + instance.existing_method + verify_all + rescue RSpec::Mocks::MockExpectationError => e + end + + reset_all + + expect(instance.existing_method).to eq :existing_method_return_value + end end end end From af67c29b03a63e8fb7de42703258bf32ba8bb61d Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Mon, 17 Feb 2014 21:09:02 +0000 Subject: [PATCH 0487/1179] Fixup any_instance specs to call space#reset_all --- lib/rspec/mocks/any_instance/recorder.rb | 3 --- spec/rspec/mocks/any_instance_spec.rb | 6 ++++++ spec/rspec/mocks/matchers/receive_messages_spec.rb | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index fa4700fe7..2ba827ce2 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -97,9 +97,6 @@ def verify if @expectation_set && !message_chains.all_expectations_fulfilled? raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}" end - ensure - stop_all_observation! - ::RSpec::Mocks.space.remove_any_instance_recorder_for(@klass) end # @private diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index d3be7cb08..889b4c090 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -765,6 +765,7 @@ def foo; end it "restores the class to its original state after each example when no instance is created" do space.verify_all + space.reset_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) @@ -774,6 +775,7 @@ def foo; end klass.new.existing_method space.verify_all + space.reset_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) @@ -784,6 +786,7 @@ def foo; end klass.new.existing_method space.verify_all + space.reset_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) @@ -794,6 +797,7 @@ def foo; end before :each do klass.any_instance.stub(:private_method).and_return(:something) space.verify_all + space.reset_all end it "cleans up the backed up method" do @@ -816,6 +820,7 @@ def foo; end klass.any_instance.should_receive(:private_method).and_return(:something) klass.new.private_method space.verify_all + space.reset_all end it "cleans up the backed up method" do @@ -863,6 +868,7 @@ def foo; end klass.any_instance.should_receive(:existing_method).and_return(Object.new) klass.new.existing_method space.verify_all + space.reset_all expect(klass.new.existing_method).to eq(existing_method_return_value) end diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 821fcd6ba..66fe366bf 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -100,6 +100,7 @@ module Mocks expect_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) obj.a expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError + RSpec::Mocks.space.reset_all end it_behaves_like "complains when given blocks" From 68803e1f7eca644d0ef8c936681c02409750c56e Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 18 Feb 2014 00:25:29 +0000 Subject: [PATCH 0488/1179] Add a changelog entry for #580 --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 86fe3a877..8f16fd708 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### 3.0.0.rc1 Development [full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...master) +Bug Fixes: + +* Ensure `any_instance` expectations and mocks are torn down regardless of + expectation failures. (Sam Phippen) + ### 3.0.0.beta2 / 2014-02-17 [full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) From e319be70ee86e5c64035f6b025451c90979108c1 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 18 Feb 2014 13:17:20 +0000 Subject: [PATCH 0489/1179] Cleanup any-instance specs --- lib/rspec/mocks/space.rb | 4 ---- spec/rspec/mocks/any_instance_spec.rb | 21 +++++++++------------ spec/rspec/mocks/space_spec.rb | 10 ---------- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 3573613e0..c685a2032 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -81,10 +81,6 @@ def any_instance_recorder_for(klass) end end - def remove_any_instance_recorder_for(klass) - any_instance_recorders.delete(klass.__id__) - end - def proxies_of(klass) proxies.values.select { |proxy| klass === proxy.object } end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 889b4c090..179a5a4cf 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -764,8 +764,7 @@ def foo; end end it "restores the class to its original state after each example when no instance is created" do - space.verify_all - space.reset_all + verify_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) @@ -774,8 +773,7 @@ def foo; end it "restores the class to its original state after each example when one instance is created" do klass.new.existing_method - space.verify_all - space.reset_all + verify_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) @@ -785,8 +783,7 @@ def foo; end klass.new.existing_method klass.new.existing_method - space.verify_all - space.reset_all + verify_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) @@ -796,8 +793,8 @@ def foo; end context "private methods" do before :each do klass.any_instance.stub(:private_method).and_return(:something) - space.verify_all - space.reset_all + + verify_all end it "cleans up the backed up method" do @@ -819,8 +816,8 @@ def foo; end before :each do klass.any_instance.should_receive(:private_method).and_return(:something) klass.new.private_method - space.verify_all - space.reset_all + + verify_all end it "cleans up the backed up method" do @@ -867,8 +864,8 @@ def foo; end it "ensures that the next spec does not see that expectation" do klass.any_instance.should_receive(:existing_method).and_return(Object.new) klass.new.existing_method - space.verify_all - space.reset_all + + verify_all expect(klass.new.existing_method).to eq(existing_method_return_value) end diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index b37c144b4..a04e9246e 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -224,16 +224,6 @@ def create_generations }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /Diff/) end - it 'removes an any_instance_recorder when requested' do - klass = Class.new - - space.any_instance_recorder_for(klass) - - expect { - space.remove_any_instance_recorder_for(klass) - }.to change { space.any_instance_recorders.size }.by(-1) - end - def in_new_space_scope RSpec::Mocks.setup yield From 47d647335553855547535ac1bbc7c229814904f7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 19 Feb 2014 15:06:55 -0800 Subject: [PATCH 0490/1179] "full changelog" => "Full Changelog" --- Changelog.md | 74 ++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8f16fd708..b0f22e32c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.0.0.rc1 Development -[full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...master) Bug Fixes: @@ -7,7 +7,7 @@ Bug Fixes: expectation failures. (Sam Phippen) ### 3.0.0.beta2 / 2014-02-17 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) Breaking Changes for 3.0.0: @@ -74,7 +74,7 @@ Bug Fixes: behavior. (Maurício Linhares) ### 3.0.0.beta1 / 2013-11-07 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) Breaking Changes for 3.0.0: @@ -138,7 +138,7 @@ Bug Fixes: (Nick DeLuca) ### 2.99.0.beta2 / 2014-02-17 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v2.99.0.beta2) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v2.99.0.beta2) Deprecations: @@ -153,7 +153,7 @@ Deprecations: * Deprecate `and_return { value }` and `and_return` without arguments. (Yuji Nakayama) ### 2.99.0.beta1 / 2013-11-07 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.99.0.beta1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.99.0.beta1) Deprecations @@ -178,7 +178,7 @@ Enhancements: blocks. (Sam Phippen) ### 2.14.5 / 2014-02-01 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.14.5) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.14.5) Bug Fixes: @@ -188,7 +188,7 @@ Bug Fixes: receives a block, too. (Myron Marston) ### 2.14.4 / 2013-10-15 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.3...v2.14.4) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.3...v2.14.4) Bug Fixes: @@ -198,7 +198,7 @@ Bug Fixes: the usage of modifications (`and_return` etc) (Jon Rowe) ### 2.14.3 / 2013-08-08 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) Bug Fixes: @@ -206,7 +206,7 @@ Bug Fixes: a prepended Module (Bradley Schaefer) ### 2.14.2 / 2013-07-30 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.1...v2.14.2) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.1...v2.14.2) Bug Fixes: @@ -216,7 +216,7 @@ Bug Fixes: return a test double rather than stub a method (Myron Marston). ### 2.14.1 / 2013-07-07 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) Bug Fixes: @@ -228,7 +228,7 @@ Bug Fixes: in 3.0. (Myron Marston) ### 2.14.0 / 2013-07-06 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0.rc1...v2.14.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0.rc1...v2.14.0) Enhancements: @@ -252,7 +252,7 @@ Bug Fixes: similar preloading gems (Andy Lindeman). ### 2.14.0.rc1 / 2013-05-27 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) Enhancements: @@ -302,7 +302,7 @@ Deprecations * Deprecate `at_least(0).times` and `any_number_of_times` (Michi Huber). ### 2.13.1 / 2013-04-06 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.13.1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.13.1) Bug fixes @@ -310,7 +310,7 @@ Bug fixes `and_yield`, `and_raise`, `and_return` or `and_throw` (Myron Marston). ### 2.13.0 / 2013-02-23 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.2...v2.13.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.2...v2.13.0) Bug fixes @@ -340,7 +340,7 @@ Bug fixes (Myron Marston). ### 2.12.2 / 2013-01-27 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.1...v.2.12.2) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.1...v.2.12.2) Bug fixes @@ -355,7 +355,7 @@ Bug fixes versions (Andy Lindeman). ### 2.12.1 / 2012-12-21 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.0...v2.12.1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.0...v2.12.1) Bug fixes @@ -367,7 +367,7 @@ Bug fixes work properly with constant strings that are prefixed with `::` (Myron Marston). ### 2.12.0 / 2012-11-12 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.3...v2.12.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.3...v2.12.0) Enhancements @@ -383,7 +383,7 @@ Deprecations: (Neha Kumari) ### 2.11.3 / 2012-09-19 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.2...v2.11.3) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.2...v2.11.3) Bug fixes @@ -397,7 +397,7 @@ Bug fixes extra time with the wrong arguments (Myron Marston). ### 2.11.2 / 2012-08-11 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.1...v2.11.2) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.1...v2.11.2) Bug fixes @@ -409,7 +409,7 @@ Bug fixes Marston) ### 2.11.1 / 2012-07-09 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.0...v2.11.1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.0...v2.11.1) Bug fixes @@ -419,7 +419,7 @@ Bug fixes overriden with the null object implementation--`return self`). (Myron Marston) ### 2.11.0 / 2012-07-07 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.1...v2.11.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.1...v2.11.0) Enhancements @@ -444,7 +444,7 @@ Bug fixes `Exception` by default, just like ruby does. (Andrew Marshall) ### 2.10.1 / 2012-05-05 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.0...v2.10.1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.0...v2.10.1) Bug fixes @@ -454,14 +454,14 @@ Bug fixes * fixed failure of `object.should_not_receive(:message).and_return value` ### 2.10.0 / 2012-05-03 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.9.0...v2.10.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.9.0...v2.10.0) Bug fixes * fail fast when an `exactly` or `at_most` expectation is exceeded ### 2.9.0 / 2012-03-17 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0...v2.9.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0...v2.9.0) Enhancements @@ -476,19 +476,19 @@ Bug fixes ### 2.8.0 / 2012-01-04 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0.rc2...v2.8.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0.rc2...v2.8.0) No changes for this release. Just releasing with the other rspec gems. ### 2.8.0.rc2 / 2011-12-19 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0.rc1...v2.8.0.rc2) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0.rc1...v2.8.0.rc2) No changes for this release. Just releasing with the other rspec gems. ### 2.8.0.rc1 / 2011-11-06 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.7.0...v2.8.0.rc1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.7.0...v2.8.0.rc1) Enhancements @@ -496,7 +496,7 @@ Enhancements ### 2.7.0 / 2011-10-16 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.6.0...v2.7.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.6.0...v2.7.0) Enhancements @@ -519,7 +519,7 @@ Bug fixes ### 2.6.0 / 2011-05-12 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.5.0...v2.6.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.5.0...v2.6.0) Enhancements @@ -533,7 +533,7 @@ Bug fixes ### 2.5.0 / 2011-02-05 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.4.0...v2.5.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.4.0...v2.5.0) Bug fixes @@ -544,14 +544,14 @@ Bug fixes ### 2.4.0 / 2011-01-02 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.3.0...v2.4.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.3.0...v2.4.0) No functional changes in this release, which was made to align with the rspec-core-2.4.0 release. ### 2.3.0 / 2010-12-12 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.2.0...v2.3.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.2.0...v2.3.0) Bug fixes @@ -560,7 +560,7 @@ Bug fixes ### 2.2.0 / 2010-11-28 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.1.0...v2.2.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.1.0...v2.2.0) Enhancements @@ -574,7 +574,7 @@ Bug fix ### 2.1.0 / 2010-11-07 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.1...v2.1.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.1...v2.1.0) Bug fixes @@ -582,11 +582,11 @@ Bug fixes ### 2.0.0 / 2010-10-10 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.22...v2.0.0) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.22...v2.0.0) ### 2.0.0.rc / 2010-10-05 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.22...v2.0.0.rc) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.22...v2.0.0.rc) Enhancements @@ -600,7 +600,7 @@ Bug fixes ### 2.0.0.beta.22 / 2010-09-12 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.20...v2.0.0.beta.22) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.20...v2.0.0.beta.22) Bug fixes From 8c129d74eeafa4190b166c367220380a5d18fdcd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 19 Feb 2014 15:54:38 -0800 Subject: [PATCH 0491/1179] Remove obselete `Upgrade.md` file. --- features/Upgrade.md | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 features/Upgrade.md diff --git a/features/Upgrade.md b/features/Upgrade.md deleted file mode 100644 index 4d9556fa6..000000000 --- a/features/Upgrade.md +++ /dev/null @@ -1,22 +0,0 @@ -## rspec-mocks-2.6 - -### `any_instance` - -Set method stubs and message expectations on any instance of a class: - - class Foo; end - Foo.any_instance.stub(:bar) { 'bar' } - Foo.new.bar # => 'bar' - -## rspec-mocks-2.2 - -### `require "rspec/mocks/standalone"` - -Sets up top-level environment to explore rspec-mocks. Mostly useful in irb: - - $ irb - > require 'rspec/mocks/standalone' - > foo = double() - > foo.stub(:bar) { :baz } - > foo.bar - => :baz From 71f633c2abbf302ed5ed681ddb654bc8d8dcff95 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 20 Feb 2014 08:11:04 -0800 Subject: [PATCH 0492/1179] Add 2.14.6 release notes. [ci skip] --- Changelog.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index b0f22e32c..8203414ce 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,11 +1,6 @@ ### 3.0.0.rc1 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...master) -Bug Fixes: - -* Ensure `any_instance` expectations and mocks are torn down regardless of - expectation failures. (Sam Phippen) - ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) @@ -177,6 +172,14 @@ Enhancements: * Add a config option to yield the receiver to `any_instance` implementation blocks. (Sam Phippen) +### 2.14.6 / 2014-02-20 +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.5...v2.14.6) + +Bug Fixes: + +* Ensure `any_instance` method stubs and expectations are torn down regardless of + expectation failures. (Sam Phippen) + ### 2.14.5 / 2014-02-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.14.5) From d81118d2b7269468369d249f3cc257bb0d545b0f Mon Sep 17 00:00:00 2001 From: Justin Coyne Date: Wed, 18 Dec 2013 20:15:07 -0600 Subject: [PATCH 0493/1179] Log a warning when methods are reset on a frozen object --- Changelog.md | 3 +++ lib/rspec/mocks/method_double.rb | 10 ++++++++++ spec/rspec/mocks/matchers/receive_spec.rb | 11 +++++++++++ 3 files changed, 24 insertions(+) diff --git a/Changelog.md b/Changelog.md index 8203414ce..fbfc0fdcf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -37,6 +37,9 @@ Enhancements: Bug Fixes: +* Instead of crashing when cleaning up stub methods on a frozen object, it now + issues a warning explaining that it's impossible to clean up the stubs. + (Justin Coyne) * Fix regression in 3.0.0.beta1 that caused `double("string_name" => :value)` to stop working. (Xavier Shay) * Fix the way rspec-mocks and rspec-core interact so that if users diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 4a08945bf..5349b3624 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -16,6 +16,7 @@ def initialize(object, method_name, proxy) @method_is_proxied = false @expectations = [] @stubs = [] + @show_frozen_warnings = [] end def original_method @@ -85,6 +86,15 @@ def restore_original_method restore_original_visibility @method_is_proxied = false + rescue RuntimeError => e + frozen = object_singleton_class.frozen? + if frozen && !@show_frozen_warnings.include?(@method_name) + RSpec.warn_with "Unable to remove stub method #{@method_name} because the object was frozen.", + :call_site => nil + @show_frozen_warnings << @method_name + elsif !frozen + raise e + end end # @private diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 3b28a2601..dd39305ce 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -212,6 +212,17 @@ def receiver.method_missing(*a); end # a poor man's stub... target.to receive(:foo).and_return(:baz) expect { reset object }.to change { object.foo }.from(:baz).to(:bar) end + + context "on a frozen object" do + it "removes the method double" do + with_isolated_stderr do + target.to receive(:foo).and_return(:baz) + expect_warning_without_call_site(/Unable to remove stub method foo because the object was frozen./) + object.freeze + reset object + end + end + end end shared_examples_for "resets partial mocks of any instance cleanly" do From 3acfdb5de3e564d15dec7d1ad26d21da988bd964 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 19 Jan 2014 12:36:54 +0000 Subject: [PATCH 0494/1179] Fixup specs for frozen mock object resets --- spec/rspec/mocks/matchers/receive_spec.rb | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index dd39305ce..e203df453 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -214,13 +214,19 @@ def receiver.method_missing(*a); end # a poor man's stub... end context "on a frozen object" do - it "removes the method double" do - with_isolated_stderr do - target.to receive(:foo).and_return(:baz) - expect_warning_without_call_site(/Unable to remove stub method foo because the object was frozen./) - object.freeze - reset object - end + it "warns about being unable to remove the method double" do + target.to receive(:foo).and_return(:baz) + expect_warning_without_call_site(/Unable to remove stub method foo because the object was frozen./) + object.freeze + reset object + end + + it "includes the spec location in the warning" do + line = __LINE__ - 1 + target.to receive(:foo).and_return(:baz) + expect_warning_without_call_site(/#{__FILE__}:#{line}/) + object.freeze + reset object end end end From 9ae1dbaadde7c99e55a88d0d13cc4f4fa60c63b1 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 19 Jan 2014 12:41:59 +0000 Subject: [PATCH 0495/1179] Conditional on frozen over exceptions --- lib/rspec/mocks/method_double.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 5349b3624..a19111791 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -77,6 +77,7 @@ def proxy_method_invoked(obj, *args, &block) # @private def restore_original_method + return show_frozen_warning if object_singleton_class.frozen? return unless @method_is_proxied object_singleton_class.__send__(:remove_method, @method_name) @@ -86,14 +87,14 @@ def restore_original_method restore_original_visibility @method_is_proxied = false - rescue RuntimeError => e - frozen = object_singleton_class.frozen? - if frozen && !@show_frozen_warnings.include?(@method_name) + end + + # @private + def show_frozen_warning + if !@show_frozen_warnings.include?(@method_name) RSpec.warn_with "Unable to remove stub method #{@method_name} because the object was frozen.", :call_site => nil @show_frozen_warnings << @method_name - elsif !frozen - raise e end end From 0ea047b137f5975f2d18eaab2d2a0a6b9003844f Mon Sep 17 00:00:00 2001 From: Stephen Best Date: Wed, 12 Feb 2014 15:25:02 +0000 Subject: [PATCH 0496/1179] Make frozen warnings work --- Changelog.md | 9 ++++++--- lib/rspec/mocks/method_double.rb | 11 +++++------ lib/rspec/mocks/test_double.rb | 4 ++++ spec/rspec/mocks/matchers/receive_spec.rb | 4 ++-- spec/rspec/mocks/test_double_spec.rb | 24 +++++++++++++++++++++++ 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Changelog.md b/Changelog.md index fbfc0fdcf..f2ac19274 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,12 @@ ### 3.0.0.rc1 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...master) +Enhancements: + +* Instead of crashing when cleaning up stub methods on a frozen object, it now + issues a warning explaining that it's impossible to clean up the stubs. + (Justin Coyne and Sam Phippen) + ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) @@ -37,9 +43,6 @@ Enhancements: Bug Fixes: -* Instead of crashing when cleaning up stub methods on a frozen object, it now - issues a warning explaining that it's impossible to clean up the stubs. - (Justin Coyne) * Fix regression in 3.0.0.beta1 that caused `double("string_name" => :value)` to stop working. (Xavier Shay) * Fix the way rspec-mocks and rspec-core interact so that if users diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index a19111791..6e040cd88 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -16,7 +16,6 @@ def initialize(object, method_name, proxy) @method_is_proxied = false @expectations = [] @stubs = [] - @show_frozen_warnings = [] end def original_method @@ -91,11 +90,11 @@ def restore_original_method # @private def show_frozen_warning - if !@show_frozen_warnings.include?(@method_name) - RSpec.warn_with "Unable to remove stub method #{@method_name} because the object was frozen.", - :call_site => nil - @show_frozen_warnings << @method_name - end + RSpec.warn_with( + "WARNING: rspec-mocks was unable to restore the original `#{@method_name}` method on #{@object.inspect} because it has been frozen. If you reuse this object, `#{@method_name}` will continue to respond with its stub implementation.", + :call_site => nil, + :use_spec_location_as_call_site => true + ) end # @private diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index f77dc4028..54ceda60a 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -67,6 +67,10 @@ def __disallow_further_usage! @__expired = true end + def freeze + RSpec.warn_with("WARNING: you attempted to freeze a test double. This is explicitly a no-op as freezing doubles can lead to undesired behaviour when resetting tests.") + end + private def __initialize_as_test_double(name=nil, stubs={}) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index e203df453..f12fb3922 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -216,7 +216,7 @@ def receiver.method_missing(*a); end # a poor man's stub... context "on a frozen object" do it "warns about being unable to remove the method double" do target.to receive(:foo).and_return(:baz) - expect_warning_without_call_site(/Unable to remove stub method foo because the object was frozen./) + expect_warning_without_call_site(/rspec-mocks was unable to restore the original `foo` method on #{object.inspect}/) object.freeze reset object end @@ -224,7 +224,7 @@ def receiver.method_missing(*a); end # a poor man's stub... it "includes the spec location in the warning" do line = __LINE__ - 1 target.to receive(:foo).and_return(:baz) - expect_warning_without_call_site(/#{__FILE__}:#{line}/) + expect_warning_without_call_site(/#{RSpec::Core::Metadata.relative_path(__FILE__)}:#{line}/) object.freeze reset object end diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 4ff8dafbf..06b29eb70 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -31,6 +31,30 @@ def use; end expect { double.foo }.to raise_error(/Double "MyDouble" received/) end + describe "#freeze" do + subject { double } + + it "gives a warning" do + expect(RSpec).to receive(:warn_with).with(/freeze a test double/) + subject.freeze + end + + it "gives the correct call site for the warning" do + expect_warning_with_call_site(__FILE__, __LINE__+1) + subject.freeze + end + + it "doesn't freeze the object" do + allow(RSpec).to receive(:warn_with).with(/freeze a test double/) + double.freeze + allow(subject).to receive(:hi) + + expect { + subject.hi + }.not_to raise_error + end + end + [[:should, :expect], [:expect], [:should]].each do |syntax| context "with syntax #{syntax.inspect}" do include_context "with syntax", syntax From 888f297fc57c5ecacb7edcd9bd8ed5e62d282b4b Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Sun, 19 Jan 2014 18:10:43 +0000 Subject: [PATCH 0497/1179] Clean up remaining expectations when verifying fails --- lib/rspec/mocks/space.rb | 2 ++ spec/rspec/mocks/matchers/receive_messages_spec.rb | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index c685a2032..1efd44d72 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -57,6 +57,8 @@ def new_scope def verify_all proxies.each_value { |proxy| proxy.verify } any_instance_recorders.each_value { |recorder| recorder.verify } + ensure + reset_all end def reset_all diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 66fe366bf..be8e4b0da 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -88,6 +88,14 @@ module Mocks expect(expectation_error.backtrace[0]).to match(/#{__FILE__}:#{line}/) end + describe 'when verifying fails' do + it 'cleans up all remaining expectations' do + expect(obj).to receive_messages(:a => 1, :b => 2) + expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError + expect { RSpec::Mocks.space.verify_all }.not_to raise_error + end + end + it_behaves_like "complains when given blocks" it_behaves_like "handles partially mocked objects correctly" end From b1a151aa1cce1e16bb41ebbaa9b28611a10bfeb7 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Thu, 9 Jan 2014 11:17:34 +0000 Subject: [PATCH 0498/1179] Verify the last argument is called in receive_message_chain Prior to this, something like - expect(object).to receive_message_chain(:foo, :bar => 1) object.foo would pass, despite 'bar' not being called. --- lib/rspec/mocks/message_chain.rb | 14 +++++++------- .../mocks/matchers/receive_message_chain_spec.rb | 10 +++++++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/message_chain.rb b/lib/rspec/mocks/message_chain.rb index 6dbdd1cf4..881ed69b2 100644 --- a/lib/rspec/mocks/message_chain.rb +++ b/lib/rspec/mocks/message_chain.rb @@ -20,17 +20,17 @@ def setup_chain chain_on(matching_expectation.invoke_without_incrementing_received_count(nil), *chain, &@block) else next_in_chain = Double.new - expectation(object, chain.shift, next_in_chain) + expectation(object, chain.shift) { next_in_chain } chain_on(next_in_chain, *chain, &@block) end else - ::RSpec::Mocks.allow_message(object, chain.shift, {}, &block) + expectation(object, chain.shift, &@block) end end private - def expectation(object, message, returned_object) + def expectation(object, message, &return_block) raise NotImplementedError.new end @@ -70,8 +70,8 @@ def self.expect_chain_on(object, *chain, &blk) private - def expectation(object, message, returned_object) - ::RSpec::Mocks.expect_message(object, message, {}) { returned_object } + def expectation(object, message, &return_block) + ::RSpec::Mocks.expect_message(object, message, {}, &return_block) end end @@ -83,8 +83,8 @@ def self.stub_chain_on(object, *chain, &blk) private - def expectation(object, message, returned_object) - ::RSpec::Mocks.allow_message(object, message, {}) { returned_object } + def expectation(object, message, &return_block) + ::RSpec::Mocks.allow_message(object, message, {}, &return_block) end end end diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index 08ad7ac81..54657e18b 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -114,7 +114,7 @@ module RSpec::Mocks::Matchers expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value) end - it "raises when expect is used and the entire chain isn't called" do + it "raises when expect is used and some of the messages in the chain aren't called" do expect { expect(object).to receive_message_chain(:to_a, :farce, :length => 3) object.to_a @@ -122,6 +122,14 @@ module RSpec::Mocks::Matchers }.to raise_error(RSpec::Mocks::MockExpectationError) end + it "raises when expect is used and all but the last message in the chain are called" do + expect { + expect(object).to receive_message_chain(:foo, :bar, :baz) + object.foo.bar + verify_all + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + it "does not raise when expect is used and the entire chain is called" do expect { expect(object).to receive_message_chain(:to_a, :length => 3) From ea7bf4abb1533eb8dbc458ff2eff4e1d7640b3aa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 21 Feb 2014 16:07:24 -0800 Subject: [PATCH 0499/1179] Rename file. --- .../{null_object_mock_spec.rb => null_object_double_spec.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename spec/rspec/mocks/{null_object_mock_spec.rb => null_object_double_spec.rb} (100%) diff --git a/spec/rspec/mocks/null_object_mock_spec.rb b/spec/rspec/mocks/null_object_double_spec.rb similarity index 100% rename from spec/rspec/mocks/null_object_mock_spec.rb rename to spec/rspec/mocks/null_object_double_spec.rb From 8d2e4e570994e13991fdbb01cf0344122800d319 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 21 Feb 2014 16:09:49 -0800 Subject: [PATCH 0500/1179] Null object `to_str` should return a string. --- Changelog.md | 6 ++++++ lib/rspec/mocks/test_double.rb | 1 + spec/rspec/mocks/null_object_double_spec.rb | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/Changelog.md b/Changelog.md index f2ac19274..3df2a37f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,12 @@ Enhancements: issues a warning explaining that it's impossible to clean up the stubs. (Justin Coyne and Sam Phippen) +Bug Fixes: + +* Fix regression introduced in 3.0.0.bet2 that caused + `double.as_null_object.to_str` to return the double rather + than a string. (Myron Marston) + ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 54ceda60a..36cdc7159 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -92,6 +92,7 @@ def method_missing(message, *args, &block) case message when :to_int then return 0 when :to_a, :to_ary then return nil + when :to_str then return to_s else return self end end diff --git a/spec/rspec/mocks/null_object_double_spec.rb b/spec/rspec/mocks/null_object_double_spec.rb index 01ca4851c..a70b0264c 100644 --- a/spec/rspec/mocks/null_object_double_spec.rb +++ b/spec/rspec/mocks/null_object_double_spec.rb @@ -46,6 +46,10 @@ module Mocks @double.something end + it 'returns a string from `to_str`' do + expect(@double.to_str).to be_a(String) + end + it 'continues to return self from an explicit expectation' do @double.should_receive(:bar) expect(@double.foo.bar).to be(@double) From 1183cbf5faa37699219bf14adfc3926923f62b63 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 10:25:41 -0800 Subject: [PATCH 0501/1179] Add changelog entry for #523. [ci skip] --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3df2a37f5..4a12be9b2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,9 @@ Bug Fixes: * Fix regression introduced in 3.0.0.bet2 that caused `double.as_null_object.to_str` to return the double rather than a string. (Myron Marston) +* Fix bug in `expect(dbl).to receive_message_chain(:foo, :bar)` where it was + not setting an expectation for the last message in the chain. + (Jonathan del Strother) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) From 1cbfe4b5803f7f888810441a2c0d337af09e249b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 18:10:16 -0800 Subject: [PATCH 0502/1179] Improve docs of ExampleMethods module. --- lib/rspec/mocks/example_methods.rb | 105 +++++++++++++++++++++++++++++ lib/rspec/mocks/syntax.rb | 100 --------------------------- 2 files changed, 105 insertions(+), 100 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 7b8121652..b42435cf8 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -2,6 +2,10 @@ module RSpec module Mocks + # Contains methods intended to be used from within code examples. + # Mix this in to your test context (such as a test framework base class) + # to use rspec-mocks with your test framework. If you're using rspec-core, + # it'll take care of doing this for you. module ExampleMethods include RSpec::Mocks::ArgumentMatchers @@ -164,6 +168,107 @@ def have_received(method_name, &block) Matchers::HaveReceived.new(method_name, &block) end + # @method expect + # Used to wrap an object in preparation for setting a mock expectation + # on it. + # + # @example + # + # expect(obj).to receive(:foo).with(5).and_return(:return_value) + # + # @note This method is usually provided by rspec-expectations. However, + # if you use rspec-mocks without rspec-expectations, there's a definition + # of it that is made available here. If you disable the `:expect` syntax + # this method will be undefined. + + # @method allow + # Used to wrap an object in preparation for stubbing a method + # on it. + # + # @example + # + # allow(dbl).to receive(:foo).with(5).and_return(:return_value) + # + # @note If you disable the `:expect` syntax this method will be undefined. + + # @method expect_any_instance_of + # Used to wrap a class in preparation for setting a mock expectation + # on instances of it. + # + # @example + # + # expect_any_instance_of(MyClass).to receive(:foo) + # + # @note If you disable the `:expect` syntax this method will be undefined. + + # @method allow_any_instance_of + # Used to wrap a class in preparation for stubbing a method + # on instances of it. + # + # @example + # + # allow_any_instance_of(MyClass).to receive(:foo) + # + # @note This is only available when you have enabled the `expect` syntax. + + # @method receive + # Used to specify a message that you expect or allow an object + # to receive. The object returned by `receive` supports the same + # fluent interface that `should_receive` and `stub` have always + # supported, allowing you to constrain the arguments or number of + # times, and configure how the object should respond to the message. + # + # @example + # + # expect(obj).to receive(:hello).with("world").exactly(3).times + # + # @note If you disable the `:expect` syntax this method will be undefined. + + # @method receive_messages + # Shorthand syntax used to setup message(s), and their return value(s), + # that you expect or allow an object to receive. The method takes a hash + # of messages and their respective return values. Unlike with `receive`, + # you cannot apply further customizations using a block or the fluent + # interface. + # + # @example + # + # allow(obj).to receive_messages(:speak => "Hello World") + # allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow") + # + # @note If you disable the `:expect` syntax this method will be undefined. + + # @method receive_message_chain + # @overload receive_message_chain(method1, method2) + # @overload receive_message_chain("method1.method2") + # @overload receive_message_chain(method1, method_to_value_hash) + # + # stubs/mocks a chain of messages on an object or test double. + # + # ## Warning: + # + # Chains can be arbitrarily long, which makes it quite painless to + # violate the Law of Demeter in violent ways, so you should consider any + # use of `receive_message_chain` a code smell. Even though not all code smells + # indicate real problems (think fluent interfaces), `receive_message_chain` still + # results in brittle examples. For example, if you write + # `foo.receive_message_chain(:bar, :baz => 37)` in a spec and then the + # implementation calls `foo.baz.bar`, the stub will not work. + # + # @example + # + # allow(double).to receive_message_chain("foo.bar") { :baz } + # allow(double).to receive_message_chain(:foo, :bar => :baz) + # allow(double).to receive_message_chain(:foo, :bar) { :baz } + # + # # Given any of ^^ these three forms ^^: + # double.foo.bar # => :baz + # + # # Common use in Rails/ActiveRecord: + # allow(Article).to receive_message_chain("recent.published") { [Article.new] } + # + # @note If you disable the `:expect` syntax this method will be undefined. + # @api private def self.included(klass) klass.class_exec do diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 289911122..12bcb4e0d 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -304,106 +304,6 @@ def self.default_should_syntax_host # @return [Recorder] # # @note This is only available when you have enabled the `should` syntax. - - # @method expect - # Used to wrap an object in preparation for setting a mock expectation - # on it. - # - # @example - # - # expect(obj).to receive(:foo).with(5).and_return(:return_value) - # - # @note This method is usually provided by rspec-expectations, unless - # you are using rspec-mocks w/o rspec-expectations, in which case it - # is only made available if you enable the `expect` syntax. - - # @method allow - # Used to wrap an object in preparation for stubbing a method - # on it. - # - # @example - # - # allow(dbl).to receive(:foo).with(5).and_return(:return_value) - # - # @note This is only available when you have enabled the `expect` syntax. - - # @method expect_any_instance_of - # Used to wrap a class in preparation for setting a mock expectation - # on instances of it. - # - # @example - # - # expect_any_instance_of(MyClass).to receive(:foo) - # - # @note This is only available when you have enabled the `expect` syntax. - - # @method allow_any_instance_of - # Used to wrap a class in preparation for stubbing a method - # on instances of it. - # - # @example - # - # allow_any_instance_of(MyClass).to receive(:foo) - # - # @note This is only available when you have enabled the `expect` syntax. - - # @method receive - # Used to specify a message that you expect or allow an object - # to receive. The object returned by `receive` supports the same - # fluent interface that `should_receive` and `stub` have always - # supported, allowing you to constrain the arguments or number of - # times, and configure how the object should respond to the message. - # - # @example - # - # expect(obj).to receive(:hello).with("world").exactly(3).times - # - # @note This is only available when you have enabled the `expect` syntax. - # - # @method receive_messages - # Shorthand syntax used to setup message(s), and their return value(s), - # that you expect or allow an object to receive. The method takes a hash - # of messages and their respective return values. Unlike with `receive`, - # you cannot apply further customizations using a block or the fluent - # interface. - # - # @example - # - # allow(obj).to receive_messages(:speak => "Hello World") - # allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow") - # - # @note This is only available when you have enabled the `expect` syntax. - # - # @method receive_message_chain - # @overload receive_message_chain(method1, method2) - # @overload receive_message_chain("method1.method2") - # @overload receive_message_chain(method1, method_to_value_hash) - # - # stubs/mocks a chain of messages on an object or test double. - # - # ## Warning: - # - # Chains can be arbitrarily long, which makes it quite painless to - # violate the Law of Demeter in violent ways, so you should consider any - # use of `receive_message_chain` a code smell. Even though not all code smells - # indicate real problems (think fluent interfaces), `receive_message_chain` still - # results in brittle examples. For example, if you write - # `foo.receive_message_chain(:bar, :baz => 37)` in a spec and then the - # implementation calls `foo.baz.bar`, the stub will not work. - # - # @example - # - # allow(double).to receive_message_chain("foo.bar") { :baz } - # allow(double).to receive_message_chain(:foo, :bar => :baz) - # allow(double).to receive_message_chain(:foo, :bar) { :baz } - # - # # Given any of ^^ these three forms ^^: - # double.foo.bar # => :baz - # - # # Common use in Rails/ActiveRecord: - # allow(Article).to receive_message_chain("recent.published") { [Article.new] } - # - # @note This is only available when you have enabled the `expect` syntax. end end end From 5fe844e791717d2b87b131c1bd4f209fa4417f47 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 18:14:01 -0800 Subject: [PATCH 0503/1179] Reorganize requires. No need for a framework file that does all the requires. --- lib/rspec/mocks.rb | 35 +++++++++++++++++++++++++++++++++-- lib/rspec/mocks/framework.rb | 35 ----------------------------------- 2 files changed, 33 insertions(+), 37 deletions(-) delete mode 100644 lib/rspec/mocks/framework.rb diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 16c56743d..3c0476f70 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -1,6 +1,37 @@ -require 'rspec/mocks/framework' -require 'rspec/mocks/version' require 'rspec/support' +require 'rspec/support/caller_filter' +require 'rspec/support/warnings' + +require 'rspec/mocks/instance_method_stasher' +require 'rspec/mocks/method_double' +require 'rspec/mocks/argument_matchers' +require 'rspec/mocks/example_methods' +require 'rspec/mocks/proxy' +require 'rspec/mocks/test_double' +require 'rspec/mocks/argument_list_matcher' +require 'rspec/mocks/message_expectation' +require 'rspec/mocks/order_group' +require 'rspec/mocks/error_generator' +require 'rspec/mocks/space' +require 'rspec/mocks/extensions/marshal' +require 'rspec/mocks/any_instance/chain' +require 'rspec/mocks/any_instance/stub_chain' +require 'rspec/mocks/any_instance/stub_chain_chain' +require 'rspec/mocks/any_instance/expect_chain_chain' +require 'rspec/mocks/any_instance/expectation_chain' +require 'rspec/mocks/any_instance/message_chains' +require 'rspec/mocks/any_instance/recorder' +require 'rspec/mocks/mutate_const' +require 'rspec/mocks/matchers/have_received' +require 'rspec/mocks/matchers/receive' +require 'rspec/mocks/matchers/receive_messages' +require 'rspec/mocks/matchers/receive_message_chain' +require 'rspec/mocks/message_chain' +require 'rspec/mocks/targets' +require 'rspec/mocks/syntax' +require 'rspec/mocks/configuration' +require 'rspec/mocks/verifying_double' +require 'rspec/mocks/version' module RSpec # Contains top-level utility methods. While this contains a few diff --git a/lib/rspec/mocks/framework.rb b/lib/rspec/mocks/framework.rb deleted file mode 100644 index 223e88c33..000000000 --- a/lib/rspec/mocks/framework.rb +++ /dev/null @@ -1,35 +0,0 @@ -# Require everything except the global extensions of class and object. This -# supports wrapping rspec's mocking functionality without invading every -# object in the system. - -require 'rspec/support/caller_filter' -require 'rspec/support/warnings' -require 'rspec/mocks/instance_method_stasher' -require 'rspec/mocks/method_double' -require 'rspec/mocks/argument_matchers' -require 'rspec/mocks/example_methods' -require 'rspec/mocks/proxy' -require 'rspec/mocks/test_double' -require 'rspec/mocks/argument_list_matcher' -require 'rspec/mocks/message_expectation' -require 'rspec/mocks/order_group' -require 'rspec/mocks/error_generator' -require 'rspec/mocks/space' -require 'rspec/mocks/extensions/marshal' -require 'rspec/mocks/any_instance/chain' -require 'rspec/mocks/any_instance/stub_chain' -require 'rspec/mocks/any_instance/stub_chain_chain' -require 'rspec/mocks/any_instance/expect_chain_chain' -require 'rspec/mocks/any_instance/expectation_chain' -require 'rspec/mocks/any_instance/message_chains' -require 'rspec/mocks/any_instance/recorder' -require 'rspec/mocks/mutate_const' -require 'rspec/mocks/matchers/have_received' -require 'rspec/mocks/matchers/receive' -require 'rspec/mocks/matchers/receive_messages' -require 'rspec/mocks/matchers/receive_message_chain' -require 'rspec/mocks/message_chain' -require 'rspec/mocks/targets' -require 'rspec/mocks/syntax' -require 'rspec/mocks/configuration' -require 'rspec/mocks/verifying_double' From 47d398b46d170c52d3efe8f675914d4c351c6911 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 18:25:21 -0800 Subject: [PATCH 0504/1179] Use autoload to delay loading features until we need them. This yields about a 10% improvement in the time it takes to initially load rspec-mocks. --- benchmarks/boot_time.sh | 31 +++++++++++++++++++ lib/rspec/mocks.rb | 25 ++++++++------- lib/rspec/mocks/any_instance.rb | 7 +++++ .../matchers/expectation_customization.rb | 20 ++++++++++++ lib/rspec/mocks/matchers/receive.rb | 17 ++-------- .../mocks/matchers/receive_message_chain.rb | 2 ++ lib/rspec/mocks/targets.rb | 9 +----- 7 files changed, 76 insertions(+), 35 deletions(-) create mode 100755 benchmarks/boot_time.sh create mode 100644 lib/rspec/mocks/any_instance.rb create mode 100644 lib/rspec/mocks/matchers/expectation_customization.rb diff --git a/benchmarks/boot_time.sh b/benchmarks/boot_time.sh new file mode 100755 index 000000000..39d12d2c8 --- /dev/null +++ b/benchmarks/boot_time.sh @@ -0,0 +1,31 @@ +time (for i in {1..100}; do ruby -Ilib:../rspec-support/lib -rrspec/mocks -e ""; done) + +# 3 runs before our autoload changes +# real 0m4.497s +# user 0m3.662s +# sys 0m0.677s +# +# real 0m4.472s +# user 0m3.644s +# sys 0m0.671s +# +# real 0m4.465s +# user 0m3.640s +# sys 0m0.668s + +# 3 runs after our autoload changes: +# +# real 0m4.038s +# user 0m3.274s +# sys 0m0.609s +# +# real 0m4.038s +# user 0m3.274s +# sys 0m0.609s +# +# real 0m4.038s +# user 0m3.274s +# sys 0m0.609s + +# It's modest, but that's about a 10% improvement: an average +# of about 40ms to load rather than 45 ms to load. diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 3c0476f70..b86d6623a 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -14,19 +14,7 @@ require 'rspec/mocks/error_generator' require 'rspec/mocks/space' require 'rspec/mocks/extensions/marshal' -require 'rspec/mocks/any_instance/chain' -require 'rspec/mocks/any_instance/stub_chain' -require 'rspec/mocks/any_instance/stub_chain_chain' -require 'rspec/mocks/any_instance/expect_chain_chain' -require 'rspec/mocks/any_instance/expectation_chain' -require 'rspec/mocks/any_instance/message_chains' -require 'rspec/mocks/any_instance/recorder' require 'rspec/mocks/mutate_const' -require 'rspec/mocks/matchers/have_received' -require 'rspec/mocks/matchers/receive' -require 'rspec/mocks/matchers/receive_messages' -require 'rspec/mocks/matchers/receive_message_chain' -require 'rspec/mocks/message_chain' require 'rspec/mocks/targets' require 'rspec/mocks/syntax' require 'rspec/mocks/configuration' @@ -114,5 +102,18 @@ class << self; attr_reader :space; end # @private IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored' + + # To speed up boot time a bit, delay loading optional or rarely + # used features until their first use. + autoload :AnyInstance, "rspec/mocks/any_instance" + autoload :ExpectChain, "rspec/mocks/message_chain" + autoload :StubChain, "rspec/mocks/message_chain" + + module Matchers + autoload :HaveReceived, "rspec/mocks/matchers/have_received" + autoload :Receive, "rspec/mocks/matchers/receive" + autoload :ReceiveMessageChain, "rspec/mocks/matchers/receive_message_chain" + autoload :ReceiveMessages, "rspec/mocks/matchers/receive_messages" + end end end diff --git a/lib/rspec/mocks/any_instance.rb b/lib/rspec/mocks/any_instance.rb new file mode 100644 index 000000000..b993cbfb5 --- /dev/null +++ b/lib/rspec/mocks/any_instance.rb @@ -0,0 +1,7 @@ +require 'rspec/mocks/any_instance/chain' +require 'rspec/mocks/any_instance/stub_chain' +require 'rspec/mocks/any_instance/stub_chain_chain' +require 'rspec/mocks/any_instance/expect_chain_chain' +require 'rspec/mocks/any_instance/expectation_chain' +require 'rspec/mocks/any_instance/message_chains' +require 'rspec/mocks/any_instance/recorder' diff --git a/lib/rspec/mocks/matchers/expectation_customization.rb b/lib/rspec/mocks/matchers/expectation_customization.rb new file mode 100644 index 000000000..81e642797 --- /dev/null +++ b/lib/rspec/mocks/matchers/expectation_customization.rb @@ -0,0 +1,20 @@ +module RSpec + module Mocks + module Matchers + # @private + class ExpectationCustomization + attr_accessor :block + + def initialize(method_name, args, block) + @method_name = method_name + @args = args + @block = block + end + + def playback_onto(expectation) + expectation.__send__(@method_name, *@args, &@block) + end + end + end + end +end diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index dee28039a..ffa350b3d 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -1,3 +1,5 @@ +require 'rspec/mocks/matchers/expectation_customization' + module RSpec module Mocks module Matchers @@ -101,20 +103,5 @@ def move_block_to_last_customization(block) end end end - - # @private - class ExpectationCustomization - attr_accessor :block - - def initialize(method_name, args, block) - @method_name = method_name - @args = args - @block = block - end - - def playback_onto(expectation) - expectation.__send__(@method_name, *@args, &@block) - end - end end end diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index 07108816d..c8bea6343 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -1,3 +1,5 @@ +require 'rspec/mocks/matchers/expectation_customization' + module RSpec module Mocks module Matchers diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index a466c2e29..19aa143c6 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -37,16 +37,9 @@ def self.disallow_negation(method_name) private def matcher_allowed?(matcher) - ALLOWED_MATCHERS.include?(matcher.class) + matcher.class.name.start_with?("RSpec::Mocks::Matchers".freeze) end - #@api private - ALLOWED_MATCHERS = [ - Matchers::Receive, - Matchers::ReceiveMessages, - Matchers::ReceiveMessageChain, - ] - def define_matcher(matcher, name, &block) matcher.__send__(name, @target, &block) end From 004af10a66060170c12d09fae1f33e7a6ecc2722 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 19:10:20 -0800 Subject: [PATCH 0505/1179] Use dedented style for `private`. --- lib/rspec/mocks/message_chain.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/message_chain.rb b/lib/rspec/mocks/message_chain.rb index 881ed69b2..f6fc44d61 100644 --- a/lib/rspec/mocks/message_chain.rb +++ b/lib/rspec/mocks/message_chain.rb @@ -68,7 +68,7 @@ def self.expect_chain_on(object, *chain, &blk) new(object, *chain, &blk).setup_chain end - private + private def expectation(object, message, &return_block) ::RSpec::Mocks.expect_message(object, message, {}, &return_block) @@ -81,7 +81,7 @@ def self.stub_chain_on(object, *chain, &blk) new(object, *chain, &blk).setup_chain end - private + private def expectation(object, message, &return_block) ::RSpec::Mocks.allow_message(object, message, {}, &return_block) From d06285046685aecb1b6659b41ff8e98120519fda Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 19:14:59 -0800 Subject: [PATCH 0506/1179] Remove duplicate constant that we don't need. --- lib/rspec/mocks/method_double.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 6e040cd88..5a0a024c1 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -202,9 +202,6 @@ def remove_single_stub(stub) def raise_method_not_stubbed_error raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" end - - # @private - IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored' end end end From d9343986316b78baa9e3fb386e20da69f3052205 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 19:15:07 -0800 Subject: [PATCH 0507/1179] Remove excess comment. --- lib/rspec/mocks/message_expectation.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index dd97a65b2..ad5b9ffa2 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -68,8 +68,6 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub self.inner_implementation_action = implementation_block end - # @private - # @private def expected_args @argument_list_matcher.expected_args From bc573f40375e7d048c7a762132eea36a2dd59416 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 21:47:46 -0800 Subject: [PATCH 0508/1179] Remove `TestDouble.extend_onto`. This was added in #117 to support using a module as a pure test double by extending `TestDouble` onto the module. Since then, I've realized we can get the same behavior by subclassing module: class MyModuleTestDouble < Module include TestDouble end ...and that's what we do for class verifying doubles now. Given that, there's no need to keep this API around anymore. --- Changelog.md | 4 +++ lib/rspec/mocks/test_double.rb | 33 +++++--------------- lib/rspec/mocks/verifying_double.rb | 7 ++--- spec/rspec/mocks/test_double_spec.rb | 37 +++-------------------- spec/rspec/mocks/verifying_double_spec.rb | 25 +++++++++++++++ 5 files changed, 44 insertions(+), 62 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4a12be9b2..96368bc36 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,10 @@ ### 3.0.0.rc1 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...master) +Breaking Changes for 3.0.0: + +* Remove `RSpec::Mocks::TestDouble.extend_onto`. (Myron Marston) + Enhancements: * Instead of crashing when cleaning up stub methods on a frozen object, it now diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 36cdc7159..af067e546 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -4,23 +4,17 @@ module Mocks # includes this module, and it is provided for cases where you want a # pure test double without subclassing RSpec::Mocks::Double. module TestDouble - # Extends the TestDouble module onto the given object and - # initializes it as a test double. - # - # @example - # - # module = Module.new - # RSpec::Mocks::TestDouble.extend_onto(module, "MyMixin", :foo => "bar") - # module.foo #=> "bar" - def self.extend_onto(object, name=nil, stubs={}) - object.extend self - object.send(:__initialize_as_test_double, name, stubs) - end - # Creates a new test double with a `name` (that will be used in error # messages only) def initialize(name=nil, stubs={}) - __initialize_as_test_double(name, stubs) + @__expired = false + if Hash === name && stubs.empty? + stubs = name + @name = nil + else + @name = name + end + assign_stubs(stubs) end # Tells the object to respond to all messages. If specific stub values @@ -73,17 +67,6 @@ def freeze private - def __initialize_as_test_double(name=nil, stubs={}) - @__expired = false - if Hash === name && stubs.empty? - stubs = name - @name = nil - else - @name = name - end - assign_stubs(stubs) - end - def method_missing(message, *args, &block) proxy = __mock_proxy proxy.record_message_received(message, *args, &block) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 97c49c712..c0e1cf902 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -48,7 +48,7 @@ def send(name, *args, &block) __send__(name, *args, &block) end - def __initialize_as_test_double(*args) + def initialize(*args) super @__sending_message = nil end @@ -65,7 +65,7 @@ class InstanceVerifyingDouble def initialize(doubled_module, *args) @doubled_module = doubled_module - __initialize_as_test_double( + super( "#{doubled_module.description} (instance)", *args ) @@ -88,8 +88,7 @@ module ObjectVerifyingDoubleMethods def initialize(doubled_module, *args) @doubled_module = doubled_module - - __initialize_as_test_double(doubled_module.description, *args) + super(doubled_module.description, *args) end def __build_mock_proxy(order_group) diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 06b29eb70..772c8dc2a 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -3,34 +3,6 @@ module RSpec module Mocks describe TestDouble do - before(:all) do - Module.class_exec do - private - def use; end - end - end - - after(:all) do - Module.class_exec do - undef use - end - end - - it 'can be extended onto a module to make it a pure test double that can mock private methods' do - double = Module.new - double.stub(:use) - expect { double.use }.to raise_error(/private method `use' called/) - - double = Module.new { TestDouble.extend_onto(self) } - double.should_receive(:use).and_return(:ok) - expect(double.use).to be(:ok) - end - - it 'sets the test double name when a name is passed' do - double = Module.new { TestDouble.extend_onto(self, "MyDouble") } - expect { double.foo }.to raise_error(/Double "MyDouble" received/) - end - describe "#freeze" do subject { double } @@ -58,13 +30,12 @@ def use; end [[:should, :expect], [:expect], [:should]].each do |syntax| context "with syntax #{syntax.inspect}" do include_context "with syntax", syntax + it 'stubs the methods passed in the stubs hash' do - double = Module.new do - TestDouble.extend_onto(self, "MyDouble", :a => 5, :b => 10) - end + dbl = double("MyDouble", :a => 5, :b => 10) - expect(double.a).to eq(5) - expect(double.b).to eq(10) + expect(dbl.a).to eq(5) + expect(dbl.b).to eq(10) end end end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 553e12620..26803ea00 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -316,6 +316,31 @@ module Mocks allow(o).to receive(:undefined_instance_method).with(:arg).and_return(1) expect(o.undefined_instance_method(:arg)).to eq(1) end + + context "when stubbing a private module method" do + before(:all) do + Module.class_exec do + private + def use; end + end + end + + after(:all) do + Module.class_exec do + undef use + end + end + + it 'can mock private module methods' do + double = Module.new + allow(double).to receive(:use) + expect { double.use }.to raise_error(/private method `use' called/) + + double = class_double("NonloadedClass") + expect(double).to receive(:use).and_return(:ok) + expect(double.use).to be(:ok) + end + end end describe 'when doubled class is loaded' do From 0bce36152b2ccff4b49f5fc4b292e43aac245f17 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 23:36:54 -0800 Subject: [PATCH 0509/1179] Prefer dedented `private`. --- lib/rspec/mocks/any_instance/expect_chain_chain.rb | 2 +- lib/rspec/mocks/any_instance/expectation_chain.rb | 5 +++-- lib/rspec/mocks/any_instance/message_chains.rb | 2 +- lib/rspec/mocks/any_instance/stub_chain.rb | 2 +- lib/rspec/mocks/any_instance/stub_chain_chain.rb | 2 +- lib/rspec/mocks/matchers/receive_message_chain.rb | 2 +- lib/rspec/mocks/matchers/receive_messages.rb | 1 - 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/any_instance/expect_chain_chain.rb b/lib/rspec/mocks/any_instance/expect_chain_chain.rb index da856efe4..0d134025f 100644 --- a/lib/rspec/mocks/any_instance/expect_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/expect_chain_chain.rb @@ -16,7 +16,7 @@ def playback!(instance) super.tap { @expectation_fulfilled = true } end - private + private def create_message_expectation_on(instance) ::RSpec::Mocks::ExpectChain.expect_chain_on(instance, *@expectation_args, &@expectation_block) diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 82c3015d7..5c4e848b1 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -12,7 +12,8 @@ def initialize(*args, &block) super end - private + private + def verify_invocation_order(rspec_method_name, *args, &block) end end @@ -20,7 +21,7 @@ def verify_invocation_order(rspec_method_name, *args, &block) # @api private class PositiveExpectationChain < ExpectationChain - private + private def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.space.proxy_for(instance) diff --git a/lib/rspec/mocks/any_instance/message_chains.rb b/lib/rspec/mocks/any_instance/message_chains.rb index a154b5ea9..205169b8b 100644 --- a/lib/rspec/mocks/any_instance/message_chains.rb +++ b/lib/rspec/mocks/any_instance/message_chains.rb @@ -61,7 +61,7 @@ def playback!(instance, method_name) end end - private + private def raise_if_second_instance_to_receive_message(instance) @instance_with_expectation ||= instance if ExpectationChain === instance diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index e8b45adfb..e7459c4a1 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -9,7 +9,7 @@ def expectation_fulfilled? true end - private + private def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.space.proxy_for(instance) diff --git a/lib/rspec/mocks/any_instance/stub_chain_chain.rb b/lib/rspec/mocks/any_instance/stub_chain_chain.rb index d24cfc6c0..c05696734 100644 --- a/lib/rspec/mocks/any_instance/stub_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain_chain.rb @@ -8,7 +8,7 @@ def initialize(*args) @expectation_fulfilled = false end - private + private def create_message_expectation_on(instance) ::RSpec::Mocks::StubChain.stub_chain_on(instance, *@expectation_args, &@expectation_block) diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index c8bea6343..c829b8a57 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -54,7 +54,7 @@ def setup_negative_expectation(*args) alias matches? setup_expectation alias does_not_match? setup_negative_expectation - private + private def replay_customizations(chain) @recorded_customizations.each do |customization| diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index 719e9b3e3..52c7f9bc6 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -66,7 +66,6 @@ def each_message_on(host) yield host, message, value end end - end end end From 55a21711f755c29de05947106fdde779d82d675c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 23 Feb 2014 08:01:48 -0800 Subject: [PATCH 0510/1179] Switch to `--require spec_helper` option. --- .rspec | 1 + spec/rspec/mocks/and_call_original_spec.rb | 1 - spec/rspec/mocks/and_return_spec.rb | 2 -- spec/rspec/mocks/and_yield_spec.rb | 2 -- spec/rspec/mocks/any_instance/message_chains_spec.rb | 2 -- spec/rspec/mocks/any_instance_spec.rb | 1 - spec/rspec/mocks/array_including_matcher_spec.rb | 2 -- spec/rspec/mocks/at_least_spec.rb | 2 -- spec/rspec/mocks/at_most_spec.rb | 2 -- spec/rspec/mocks/before_all_spec.rb | 2 -- spec/rspec/mocks/block_return_value_spec.rb | 2 -- spec/rspec/mocks/combining_implementation_instructions_spec.rb | 2 -- spec/rspec/mocks/configuration_spec.rb | 2 -- spec/rspec/mocks/double_spec.rb | 2 -- spec/rspec/mocks/example_methods_spec.rb | 2 -- spec/rspec/mocks/extensions/marshal_spec.rb | 2 -- spec/rspec/mocks/failing_argument_matchers_spec.rb | 2 -- spec/rspec/mocks/hash_excluding_matcher_spec.rb | 2 -- spec/rspec/mocks/hash_including_matcher_spec.rb | 2 -- spec/rspec/mocks/instance_method_stasher_spec.rb | 2 -- spec/rspec/mocks/matchers/have_received_spec.rb | 2 -- spec/rspec/mocks/matchers/receive_message_chain_spec.rb | 3 --- spec/rspec/mocks/matchers/receive_messages_spec.rb | 2 -- spec/rspec/mocks/matchers/receive_spec.rb | 2 -- spec/rspec/mocks/method_signature_verifier_spec.rb | 2 -- spec/rspec/mocks/methods_spec.rb | 2 -- spec/rspec/mocks/mock_expectation_error_spec.rb | 2 -- spec/rspec/mocks/mock_ordering_spec.rb | 2 -- spec/rspec/mocks/multiple_return_value_spec.rb | 2 -- spec/rspec/mocks/mutate_const_spec.rb | 2 -- spec/rspec/mocks/nil_expectation_warning_spec.rb | 2 -- spec/rspec/mocks/null_object_double_spec.rb | 2 -- spec/rspec/mocks/once_counts_spec.rb | 2 -- spec/rspec/mocks/options_hash_spec.rb | 2 -- spec/rspec/mocks/order_group_spec.rb | 1 - spec/rspec/mocks/partial_double_spec.rb | 2 -- spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb | 2 -- spec/rspec/mocks/passing_argument_matchers_spec.rb | 2 -- spec/rspec/mocks/precise_counts_spec.rb | 2 -- spec/rspec/mocks/record_messages_spec.rb | 2 -- spec/rspec/mocks/serialization_spec.rb | 2 -- spec/rspec/mocks/space_spec.rb | 1 - spec/rspec/mocks/stash_spec.rb | 3 --- spec/rspec/mocks/stub_chain_spec.rb | 2 -- spec/rspec/mocks/stub_implementation_spec.rb | 2 -- spec/rspec/mocks/stub_spec.rb | 2 -- spec/rspec/mocks/stubbed_message_expectations_spec.rb | 2 -- spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb | 2 -- spec/rspec/mocks/syntax_spec.rb | 2 -- spec/rspec/mocks/test_double_spec.rb | 2 -- spec/rspec/mocks/to_ary_spec.rb | 2 -- spec/rspec/mocks/twice_counts_spec.rb | 2 -- spec/rspec/mocks/verifying_double_spec.rb | 2 -- spec/rspec/mocks/verifying_message_expecation_spec.rb | 2 -- spec/rspec/mocks_spec.rb | 2 -- 55 files changed, 1 insertion(+), 106 deletions(-) diff --git a/.rspec b/.rspec index 372b5acf8..d0f72792a 100644 --- a/.rspec +++ b/.rspec @@ -1 +1,2 @@ --warnings +--require spec_helper diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 82c31b62f..66e1241a5 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'delegate' describe "and_call_original" do diff --git a/spec/rspec/mocks/and_return_spec.rb b/spec/rspec/mocks/and_return_spec.rb index 7c5c8dbda..25b7132ec 100644 --- a/spec/rspec/mocks/and_return_spec.rb +++ b/spec/rspec/mocks/and_return_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe 'and_return' do diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index aa89dedf2..f667dce25 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - describe RSpec::Mocks::Double do let(:obj) { double } diff --git a/spec/rspec/mocks/any_instance/message_chains_spec.rb b/spec/rspec/mocks/any_instance/message_chains_spec.rb index fce8c7100..b2fe19865 100644 --- a/spec/rspec/mocks/any_instance/message_chains_spec.rb +++ b/spec/rspec/mocks/any_instance/message_chains_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - describe RSpec::Mocks::AnyInstance::MessageChains do let(:recorder) { double } let(:chains) { RSpec::Mocks::AnyInstance::MessageChains.new } diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 179a5a4cf..318f82f3b 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'delegate' module RSpec diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb index e038de322..9d6521319 100644 --- a/spec/rspec/mocks/array_including_matcher_spec.rb +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks module ArgumentMatchers diff --git a/spec/rspec/mocks/at_least_spec.rb b/spec/rspec/mocks/at_least_spec.rb index ae43d10a5..574dc6043 100644 --- a/spec/rspec/mocks/at_least_spec.rb +++ b/spec/rspec/mocks/at_least_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "at_least" do diff --git a/spec/rspec/mocks/at_most_spec.rb b/spec/rspec/mocks/at_most_spec.rb index 5205d5f7d..a0a5a18a5 100644 --- a/spec/rspec/mocks/at_most_spec.rb +++ b/spec/rspec/mocks/at_most_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "at_most" do diff --git a/spec/rspec/mocks/before_all_spec.rb b/spec/rspec/mocks/before_all_spec.rb index fe71d8d3f..17f6675b8 100644 --- a/spec/rspec/mocks/before_all_spec.rb +++ b/spec/rspec/mocks/before_all_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - describe "Using rspec-mocks features in before(:all) blocks" do shared_examples_for "fails in a before(:all) block" do the_error = nil diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 540d67f45..48f0ce882 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - describe "a double declaration with a block handed to:" do describe "should_receive" do it "returns the value of executing the block" do diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 0c72dde08..9cc10606a 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "Combining implementation instructions" do diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 9ffd0c1c5..042418c33 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe Configuration do diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index b00bea824..ea4e98464 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe Double do diff --git a/spec/rspec/mocks/example_methods_spec.rb b/spec/rspec/mocks/example_methods_spec.rb index ec489e5cc..71e51f577 100644 --- a/spec/rspec/mocks/example_methods_spec.rb +++ b/spec/rspec/mocks/example_methods_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe ExampleMethods do diff --git a/spec/rspec/mocks/extensions/marshal_spec.rb b/spec/rspec/mocks/extensions/marshal_spec.rb index efe304ad8..2efd5f6cf 100644 --- a/spec/rspec/mocks/extensions/marshal_spec.rb +++ b/spec/rspec/mocks/extensions/marshal_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - describe Marshal, 'extensions' do # An object that raises when code attempts to dup it. # diff --git a/spec/rspec/mocks/failing_argument_matchers_spec.rb b/spec/rspec/mocks/failing_argument_matchers_spec.rb index 596dcc7ce..0ddcbf570 100644 --- a/spec/rspec/mocks/failing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/failing_argument_matchers_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "failing MockArgumentMatchers" do diff --git a/spec/rspec/mocks/hash_excluding_matcher_spec.rb b/spec/rspec/mocks/hash_excluding_matcher_spec.rb index 1eddff2ea..4ef86c954 100644 --- a/spec/rspec/mocks/hash_excluding_matcher_spec.rb +++ b/spec/rspec/mocks/hash_excluding_matcher_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks module ArgumentMatchers diff --git a/spec/rspec/mocks/hash_including_matcher_spec.rb b/spec/rspec/mocks/hash_including_matcher_spec.rb index 0ea90df1d..b58912532 100644 --- a/spec/rspec/mocks/hash_including_matcher_spec.rb +++ b/spec/rspec/mocks/hash_including_matcher_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks module ArgumentMatchers diff --git a/spec/rspec/mocks/instance_method_stasher_spec.rb b/spec/rspec/mocks/instance_method_stasher_spec.rb index c76fedfc7..a63924169 100644 --- a/spec/rspec/mocks/instance_method_stasher_spec.rb +++ b/spec/rspec/mocks/instance_method_stasher_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe InstanceMethodStasher do diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 42a9b41bd..8554b06c5 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe Matchers::HaveReceived do diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index 54657e18b..bac1ef88a 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -1,6 +1,3 @@ -require "spec_helper" - - module RSpec::Mocks::Matchers describe "receive_message_chain" do let(:object) { double(:object) } diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index be8e4b0da..115c50c8b 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks shared_examples_for "complains when given blocks" do diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index f12fb3922..798cf8dd9 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe Matchers::Receive do diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb index fc22cda01..cc0471f62 100644 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ b/spec/rspec/mocks/method_signature_verifier_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe MethodSignatureVerifier do diff --git a/spec/rspec/mocks/methods_spec.rb b/spec/rspec/mocks/methods_spec.rb index 74f018844..7a25fb4c7 100644 --- a/spec/rspec/mocks/methods_spec.rb +++ b/spec/rspec/mocks/methods_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "Methods added to every object" do diff --git a/spec/rspec/mocks/mock_expectation_error_spec.rb b/spec/rspec/mocks/mock_expectation_error_spec.rb index 5c2c4ea67..342a554ea 100644 --- a/spec/rspec/mocks/mock_expectation_error_spec.rb +++ b/spec/rspec/mocks/mock_expectation_error_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe 'MockExpectationError' do diff --git a/spec/rspec/mocks/mock_ordering_spec.rb b/spec/rspec/mocks/mock_ordering_spec.rb index 3754d65e5..722bce30f 100644 --- a/spec/rspec/mocks/mock_ordering_spec.rb +++ b/spec/rspec/mocks/mock_ordering_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks diff --git a/spec/rspec/mocks/multiple_return_value_spec.rb b/spec/rspec/mocks/multiple_return_value_spec.rb index 236c7e5de..b57ffac85 100644 --- a/spec/rspec/mocks/multiple_return_value_spec.rb +++ b/spec/rspec/mocks/multiple_return_value_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "a double stubbed with multiple return values" do diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 4965d70a7..8db61a898 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - TOP_LEVEL_VALUE_CONST = 7 class TestClass diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index 875bd03e8..4de403f9d 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "an expectation set on nil" do diff --git a/spec/rspec/mocks/null_object_double_spec.rb b/spec/rspec/mocks/null_object_double_spec.rb index a70b0264c..5c134df10 100644 --- a/spec/rspec/mocks/null_object_double_spec.rb +++ b/spec/rspec/mocks/null_object_double_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "a double _not_ acting as a null object" do diff --git a/spec/rspec/mocks/once_counts_spec.rb b/spec/rspec/mocks/once_counts_spec.rb index f57109d89..e5b643e92 100644 --- a/spec/rspec/mocks/once_counts_spec.rb +++ b/spec/rspec/mocks/once_counts_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "#once" do diff --git a/spec/rspec/mocks/options_hash_spec.rb b/spec/rspec/mocks/options_hash_spec.rb index 25785d38e..05ba06684 100644 --- a/spec/rspec/mocks/options_hash_spec.rb +++ b/spec/rspec/mocks/options_hash_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "calling :should_receive with an options hash" do diff --git a/spec/rspec/mocks/order_group_spec.rb b/spec/rspec/mocks/order_group_spec.rb index 3c4de181c..adf052a48 100644 --- a/spec/rspec/mocks/order_group_spec.rb +++ b/spec/rspec/mocks/order_group_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' describe 'OrderGroup' do let(:order_group) { ::RSpec::Mocks::OrderGroup.new } diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 5a54d7dde..06e2ad5ee 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "A partial double" do diff --git a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb index 5ce0398d5..e206d135d 100644 --- a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb +++ b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec::Mocks describe "PartialDoubleUsingMocksDirectly" do let(:klass) do diff --git a/spec/rspec/mocks/passing_argument_matchers_spec.rb b/spec/rspec/mocks/passing_argument_matchers_spec.rb index c5fad81f7..4cd6c7349 100644 --- a/spec/rspec/mocks/passing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/passing_argument_matchers_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "Passing argument matchers" do diff --git a/spec/rspec/mocks/precise_counts_spec.rb b/spec/rspec/mocks/precise_counts_spec.rb index 32feca6f7..ba167e78d 100644 --- a/spec/rspec/mocks/precise_counts_spec.rb +++ b/spec/rspec/mocks/precise_counts_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "PreciseCounts" do diff --git a/spec/rspec/mocks/record_messages_spec.rb b/spec/rspec/mocks/record_messages_spec.rb index 6ba1273c6..0a59b2d97 100644 --- a/spec/rspec/mocks/record_messages_spec.rb +++ b/spec/rspec/mocks/record_messages_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "a mock" do diff --git a/spec/rspec/mocks/serialization_spec.rb b/spec/rspec/mocks/serialization_spec.rb index a99fdf2da..3c39c7bfc 100644 --- a/spec/rspec/mocks/serialization_spec.rb +++ b/spec/rspec/mocks/serialization_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "Serialization of mocked objects" do diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index a04e9246e..f124c3f39 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' module RSpec::Mocks describe Space do diff --git a/spec/rspec/mocks/stash_spec.rb b/spec/rspec/mocks/stash_spec.rb index f18e3cbbe..668a1b74f 100644 --- a/spec/rspec/mocks/stash_spec.rb +++ b/spec/rspec/mocks/stash_spec.rb @@ -1,8 +1,5 @@ -require 'spec_helper' - module RSpec module Mocks - describe "only stashing the original method" do let(:klass) do Class.new do diff --git a/spec/rspec/mocks/stub_chain_spec.rb b/spec/rspec/mocks/stub_chain_spec.rb index a566e5abb..7c30286fd 100644 --- a/spec/rspec/mocks/stub_chain_spec.rb +++ b/spec/rspec/mocks/stub_chain_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "A chained method stub" do diff --git a/spec/rspec/mocks/stub_implementation_spec.rb b/spec/rspec/mocks/stub_implementation_spec.rb index 5abda0d0f..0e58b96bf 100644 --- a/spec/rspec/mocks/stub_implementation_spec.rb +++ b/spec/rspec/mocks/stub_implementation_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "stub implementation" do diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 5caeb84ec..d22f0d24b 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "A method stub" do diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index 445bc0702..aedec6bbd 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - describe "expection set on previously stubbed method" do it "fails if message is not received after expectation is set" do double = double(:msg => nil) diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index d3e48ae2b..93a86757a 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec module Mocks diff --git a/spec/rspec/mocks/syntax_spec.rb b/spec/rspec/mocks/syntax_spec.rb index 3e2bd3b9f..51f1a238e 100644 --- a/spec/rspec/mocks/syntax_spec.rb +++ b/spec/rspec/mocks/syntax_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec::Mocks describe Syntax do context "when the should syntax is enabled on a non-default syntax host" do diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 772c8dc2a..1509df84b 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe TestDouble do diff --git a/spec/rspec/mocks/to_ary_spec.rb b/spec/rspec/mocks/to_ary_spec.rb index c1d2810d9..f1c0d68e3 100644 --- a/spec/rspec/mocks/to_ary_spec.rb +++ b/spec/rspec/mocks/to_ary_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - describe "a double receiving to_ary" do shared_examples "to_ary" do it "can be overridden with a stub" do diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index 5f772fd78..cbace3842 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe "#twice" do diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 26803ea00..f2ee55c27 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - class LoadedClass extend RSpec::Mocks::RubyFeatures diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 2fae9812b..b060e87e0 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Mocks describe VerifyingMessageExpectation do diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 69a882d20..4d8c4ea6c 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - describe RSpec::Mocks do describe ".verify" do it "delegates to the space" do From 8c513d2cbbd97f99b3759c1ed4e6be3fc77b8d7e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 25 Feb 2014 09:36:31 +1100 Subject: [PATCH 0511/1179] remove test files from gemspec to speed up dl/s --- rspec-mocks.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 6c70dcc3a..20eb7a3d4 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.files = `git ls-files -- lib/*`.split("\n") s.files += %w[README.md License.txt Changelog.md .yardopts .document] - s.test_files = `git ls-files -- {spec,features}/*`.split("\n") + s.test_files = [] s.rdoc_options = ["--charset=UTF-8"] s.require_path = "lib" From 6618d3cd6f0d15b0650e5b8eb522f1fda57782ed Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 25 Feb 2014 12:01:07 +1100 Subject: [PATCH 0512/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 3 ++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 973f30b15..b03b6d161 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-02-12T08:24:06+11:00 from the rspec-dev repo. +# This file was generated on 2014-02-25T12:00:45+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: "script/clone_all_rspec_repos" @@ -10,6 +10,7 @@ rvm: - 1.9.3 - 2.0.0 - 2.1.0 + - 2.1.1 - ruby-head - ree - jruby-18mode diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index c54006df7..9d42ac4e6 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-02-12T08:24:06+11:00 from the rspec-dev repo. +# This file was generated on 2014-02-25T12:00:45+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 761651f75..c94eb3c8c 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-02-12T08:24:06+11:00 from the rspec-dev repo. +# This file was generated on 2014-02-25T12:00:45+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index 3befcf851..437d75860 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-02-12T08:24:06+11:00 from the rspec-dev repo. +# This file was generated on 2014-02-25T12:00:45+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From 1fb1ff36df480bb1254eb2d6a7ba1429e02d1b40 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 24 Feb 2014 23:26:56 -0800 Subject: [PATCH 0513/1179] Leverage new optimized require from rspec-support. --- lib/rspec/mocks.rb | 44 ++++++++++--------- lib/rspec/mocks/any_instance.rb | 16 ++++--- lib/rspec/mocks/argument_list_matcher.rb | 3 ++ lib/rspec/mocks/example_methods.rb | 2 +- lib/rspec/mocks/matchers/receive.rb | 2 +- .../mocks/matchers/receive_message_chain.rb | 2 +- lib/rspec/mocks/method_signature_verifier.rb | 2 +- lib/rspec/mocks/verifying_double.rb | 2 +- .../mocks/verifying_message_expecation.rb | 2 +- lib/rspec/mocks/verifying_proxy.rb | 4 +- 10 files changed, 44 insertions(+), 35 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index b86d6623a..4e613a5d1 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -1,25 +1,29 @@ require 'rspec/support' -require 'rspec/support/caller_filter' -require 'rspec/support/warnings' +RSpec::Support.require_rspec_support 'caller_filter' +RSpec::Support.require_rspec_support 'warnings' -require 'rspec/mocks/instance_method_stasher' -require 'rspec/mocks/method_double' -require 'rspec/mocks/argument_matchers' -require 'rspec/mocks/example_methods' -require 'rspec/mocks/proxy' -require 'rspec/mocks/test_double' -require 'rspec/mocks/argument_list_matcher' -require 'rspec/mocks/message_expectation' -require 'rspec/mocks/order_group' -require 'rspec/mocks/error_generator' -require 'rspec/mocks/space' -require 'rspec/mocks/extensions/marshal' -require 'rspec/mocks/mutate_const' -require 'rspec/mocks/targets' -require 'rspec/mocks/syntax' -require 'rspec/mocks/configuration' -require 'rspec/mocks/verifying_double' -require 'rspec/mocks/version' +RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f } + +%w[ + instance_method_stasher + method_double + argument_matchers + example_methods + proxy + test_double + argument_list_matcher + message_expectation + order_group + error_generator + space + extensions/marshal + mutate_const + targets + syntax + configuration + verifying_double + version +].each { |name| RSpec::Support.require_rspec_mocks name } module RSpec # Contains top-level utility methods. While this contains a few diff --git a/lib/rspec/mocks/any_instance.rb b/lib/rspec/mocks/any_instance.rb index b993cbfb5..1cae54ae6 100644 --- a/lib/rspec/mocks/any_instance.rb +++ b/lib/rspec/mocks/any_instance.rb @@ -1,7 +1,9 @@ -require 'rspec/mocks/any_instance/chain' -require 'rspec/mocks/any_instance/stub_chain' -require 'rspec/mocks/any_instance/stub_chain_chain' -require 'rspec/mocks/any_instance/expect_chain_chain' -require 'rspec/mocks/any_instance/expectation_chain' -require 'rspec/mocks/any_instance/message_chains' -require 'rspec/mocks/any_instance/recorder' +%w[ + any_instance/chain + any_instance/stub_chain + any_instance/stub_chain_chain + any_instance/expect_chain_chain + any_instance/expectation_chain + any_instance/message_chains + any_instance/recorder +].each { |f| RSpec::Support.require_rspec_mocks(f) } diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index e9b8e99d2..f65af7af6 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -1,3 +1,6 @@ +# We intentionally do not use the `RSpec::Support.require...` methods +# here so that this file can be loaded individually, as documented +# below. require 'rspec/mocks/argument_matchers' require 'rspec/support/fuzzy_matcher' diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index b42435cf8..799c11c3e 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/object_reference' +RSpec::Support.require_rspec_mocks 'object_reference' module RSpec module Mocks diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index ffa350b3d..9a12fab16 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/matchers/expectation_customization' +RSpec::Support.require_rspec_mocks 'matchers/expectation_customization' module RSpec module Mocks diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index c829b8a57..51930371f 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/matchers/expectation_customization' +RSpec::Support.require_rspec_mocks 'matchers/expectation_customization' module RSpec module Mocks diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index d457153c8..8edd378e4 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/ruby_features' +RSpec::Support.require_rspec_mocks 'ruby_features' module RSpec module Mocks diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index c0e1cf902..a0ab257ec 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/verifying_proxy' +RSpec::Support.require_rspec_mocks 'verifying_proxy' require 'stringio' module RSpec diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 39f0254ab..99279d633 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -1,4 +1,4 @@ -require 'rspec/mocks/method_signature_verifier' +RSpec::Support.require_rspec_mocks 'method_signature_verifier' module RSpec module Mocks diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 36d1ae00d..fedb513bf 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -1,5 +1,5 @@ -require 'rspec/mocks/verifying_message_expecation' -require 'rspec/mocks/method_reference' +RSpec::Support.require_rspec_mocks 'verifying_message_expecation' +RSpec::Support.require_rspec_mocks 'method_reference' module RSpec module Mocks From a3fe4272d2e1df84c80a64d3548376557b125119 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 25 Feb 2014 21:42:47 -0800 Subject: [PATCH 0514/1179] Fix typo. [ci skip] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 96368bc36..f18363819 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,7 +13,7 @@ Enhancements: Bug Fixes: -* Fix regression introduced in 3.0.0.bet2 that caused +* Fix regression introduced in 3.0.0.beta2 that caused `double.as_null_object.to_str` to return the double rather than a string. (Myron Marston) * Fix bug in `expect(dbl).to receive_message_chain(:foo, :bar)` where it was From 2634348ceb011a0cff43cc6e9acc4d115f358b55 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 27 Feb 2014 09:51:55 +1100 Subject: [PATCH 0515/1179] remove constant stubber const --- lib/rspec/mocks/mutate_const.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index 377b270ac..56226dc94 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -380,11 +380,5 @@ def self.raise_on_invalid_const end end end - - # Keeps backwards compatibility since we had released an rspec-mocks that - # only supported stubbing. Later, we released the hide_const feature and - # decided that the term "mutator" was a better term to wrap up the concept - # of both stubbing and hiding. - ConstantStubber = ConstantMutator end end From 70afdc228b4abae60cc95726596c71e5f3ed594d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 27 Feb 2014 18:43:06 +1100 Subject: [PATCH 0516/1179] 2.99 rc1 changelog [skip ci] --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index 96368bc36..5e5b9dc49 100644 --- a/Changelog.md +++ b/Changelog.md @@ -151,6 +151,14 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.0.rc1 Development +[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta2...2-99-maintenance) + +Deprecations: + +* Deprecate `RSpec::Mocks::TestDouble.extend_onto`. (Myron Marston) +* Deprecate `RSpec::Mocks::ConstantStubber`. (Jon Rowe) + ### 2.99.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v2.99.0.beta2) From 2de265c835163ee47e00d5474e861bc7fd3fb54a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 27 Feb 2014 18:43:55 +1100 Subject: [PATCH 0517/1179] fix typo [skip ci] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 5e5b9dc49..483921365 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,7 +13,7 @@ Enhancements: Bug Fixes: -* Fix regression introduced in 3.0.0.bet2 that caused +* Fix regression introduced in 3.0.0.beta2 that caused `double.as_null_object.to_str` to return the double rather than a string. (Myron Marston) * Fix bug in `expect(dbl).to receive_message_chain(:foo, :bar)` where it was From be3b0a4b59a331f3ae3066cbb4f6885e9ca3e4e7 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 27 Feb 2014 18:44:07 +1100 Subject: [PATCH 0518/1179] changelog entry for removal of ConstantStubber [skip ci] --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 483921365..a34a558f7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Breaking Changes for 3.0.0: * Remove `RSpec::Mocks::TestDouble.extend_onto`. (Myron Marston) +* Remove `RSpec::Mocks::ConstantStubber`. (Jon Rowe) Enhancements: From c53dd99f26495055f350997a167d74b6047094bc Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 27 Feb 2014 19:00:00 +1100 Subject: [PATCH 0519/1179] Revert "2.99 rc1 changelog" [skip ci] This reverts commit 70afdc228b4abae60cc95726596c71e5f3ed594d. --- Changelog.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index a34a558f7..885187513 100644 --- a/Changelog.md +++ b/Changelog.md @@ -152,14 +152,6 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) -### 2.99.0.rc1 Development -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta2...2-99-maintenance) - -Deprecations: - -* Deprecate `RSpec::Mocks::TestDouble.extend_onto`. (Myron Marston) -* Deprecate `RSpec::Mocks::ConstantStubber`. (Jon Rowe) - ### 2.99.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v2.99.0.beta2) From f5d9fe9fa20f17236060aaba5361ad2fa4c08e50 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 26 Feb 2014 09:16:40 -0800 Subject: [PATCH 0520/1179] Migrate `should` syntax specific specs to their own file. --- ...bining_implementation_instructions_spec.rb | 6 - spec/rspec/mocks/configuration_spec.rb | 72 +-------- spec/rspec/mocks/options_hash_spec.rb | 33 ----- spec/rspec/mocks/partial_double_spec.rb | 4 - spec/rspec/mocks/should_syntax_spec.rb | 140 ++++++++++++++++++ spec/rspec/mocks/stub_spec.rb | 4 - spec/rspec/mocks/syntax_spec.rb | 17 --- 7 files changed, 141 insertions(+), 135 deletions(-) delete mode 100644 spec/rspec/mocks/options_hash_spec.rb create mode 100644 spec/rspec/mocks/should_syntax_spec.rb delete mode 100644 spec/rspec/mocks/syntax_spec.rb diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 9cc10606a..c5a1fdab5 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -124,12 +124,6 @@ def verify_combined_implementation expect(block_called).to be_truthy end - it 'returns `nil` from all terminal actions to discourage further configuration' do - expect(double.stub(:foo).and_return(1)).to be_nil - expect(double.stub(:foo).and_raise("boom")).to be_nil - expect(double.stub(:foo).and_throw(:foo)).to be_nil - end - it 'allows the terminal action to be overriden' do dbl = double stubbed_double = dbl.stub(:foo) diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 042418c33..7a14911e8 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -119,79 +119,9 @@ def sandboxed Object.new.should_not_receive(:bees) end end - - context "by default" do - before do - configure_default_syntax - end - - let(:expected_arguments) { - [ - /Using.*without explicitly enabling/, - {:replacement=>"the new `:expect` syntax or explicitly enable `:should`"} - ] - } - - it "it warns about should once, regardless of how many times it is called" do - expect(RSpec).to receive(:deprecate).with(*expected_arguments) - o = Object.new - o2 = Object.new - o.should_receive(:bees) - o2.should_receive(:bees) - - o.bees - o2.bees - end - - it "warns about should not once, regardless of how many times it is called" do - expect(RSpec).to receive(:deprecate).with(*expected_arguments) - o = Object.new - o2 = Object.new - o.should_not_receive(:bees) - o2.should_not_receive(:bees) - end - - it "warns about stubbing once, regardless of how many times it is called" do - expect(RSpec).to receive(:deprecate).with(*expected_arguments) - o = Object.new - o2 = Object.new - - o.stub(:faces) - o2.stub(:faces) - end - - it "warns about unstubbing once, regardless of how many times it is called" do - expect(RSpec).to receive(:deprecate).with(/Using.*without explicitly enabling/, - {:replacement => "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`"}) - o = Object.new - o2 = Object.new - - allow(o).to receive(:faces) - allow(o2).to receive(:faces) - - o.unstub(:faces) - o2.unstub(:faces) - end - - - it "doesn't warn about stubbing after a reset and setting should" do - expect(RSpec).not_to receive(:deprecate) - RSpec::Mocks.configuration.reset_syntaxes_to_default - RSpec::Mocks.configuration.syntax = :should - o = Object.new - o2 = Object.new - o.stub(:faces) - o2.stub(:faces) - end - - it "includes the call site in the deprecation warning" do - obj = Object.new - expect_deprecation_with_call_site(__FILE__, __LINE__ + 1) - obj.stub(:faces) - end - end end + describe "configuring rspec-mocks directly" do it_behaves_like "configuring the syntax" do def configure_syntax(syntax) diff --git a/spec/rspec/mocks/options_hash_spec.rb b/spec/rspec/mocks/options_hash_spec.rb deleted file mode 100644 index 05ba06684..000000000 --- a/spec/rspec/mocks/options_hash_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -module RSpec - module Mocks - describe "calling :should_receive with an options hash" do - it "reports the file and line submitted with :expected_from" do - begin - mock = RSpec::Mocks::Double.new("a mock") - mock.should_receive(:message, :expected_from => "/path/to/blah.ext:37") - verify mock - rescue Exception => e - ensure - expect(e.backtrace.to_s).to match(/\/path\/to\/blah.ext:37/m) - end - end - - it "uses the message supplied with :message" do - expect { - m = RSpec::Mocks::Double.new("a mock") - m.should_receive(:message, :message => "recebi nada") - verify m - }.to raise_error("recebi nada") - end - - it "uses the message supplied with :message after a similar stub" do - expect { - m = RSpec::Mocks::Double.new("a mock") - m.stub(:message) - m.should_receive(:message, :message => "from mock") - verify m - }.to raise_error("from mock") - end - end - end -end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 06e2ad5ee..9d52b6c2a 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -33,10 +33,6 @@ module Mocks ) end - it "should_not_receive returns a negative message expectation" do - expect(object.should_not_receive(:foobar)).to be_negative - end - it "should_receive mocks out the method" do object.should_receive(:foobar).with(:test_param).and_return(1) expect(object.foobar(:test_param)).to equal(1) diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb new file mode 100644 index 000000000..98d76f989 --- /dev/null +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -0,0 +1,140 @@ +RSpec.describe "Using the legacy should syntax" do + include_context "with syntax", [:should, :expect] + + describe "#stub" do + it "supports options" do + double.stub(:foo, :expected_from => "bar") + end + + it 'returns `nil` from all terminal actions to discourage further configuration' do + expect(double.stub(:foo).and_return(1)).to be_nil + expect(double.stub(:foo).and_raise("boom")).to be_nil + expect(double.stub(:foo).and_throw(:foo)).to be_nil + end + end + + describe "#should_receive" do + context "with an options hash" do + it "reports the file and line submitted with :expected_from" do + begin + mock = RSpec::Mocks::Double.new("a mock") + mock.should_receive(:message, :expected_from => "/path/to/blah.ext:37") + verify mock + rescue Exception => e + ensure + expect(e.backtrace.to_s).to match(/\/path\/to\/blah.ext:37/m) + end + end + + it "uses the message supplied with :message" do + expect { + m = RSpec::Mocks::Double.new("a mock") + m.should_receive(:message, :message => "recebi nada") + verify m + }.to raise_error("recebi nada") + end + + it "uses the message supplied with :message after a similar stub" do + expect { + m = RSpec::Mocks::Double.new("a mock") + m.stub(:message) + m.should_receive(:message, :message => "from mock") + verify m + }.to raise_error("from mock") + end + end + end + + describe "#should_not_receive" do + it "returns a negative message expectation" do + expect(Object.new.should_not_receive(:foobar)).to be_negative + end + end +end + +RSpec.context "with default syntax configuration" do + orig_syntax = nil + + before(:all) { orig_syntax = RSpec::Mocks.configuration.syntax } + after(:all) { RSpec::Mocks.configuration.syntax = orig_syntax } + before { RSpec::Mocks.configuration.reset_syntaxes_to_default } + + let(:expected_arguments) { + [ + /Using.*without explicitly enabling/, + {:replacement=>"the new `:expect` syntax or explicitly enable `:should`"} + ] + } + + it "it warns about should once, regardless of how many times it is called" do + expect(RSpec).to receive(:deprecate).with(*expected_arguments) + o = Object.new + o2 = Object.new + o.should_receive(:bees) + o2.should_receive(:bees) + + o.bees + o2.bees + end + + it "warns about should not once, regardless of how many times it is called" do + expect(RSpec).to receive(:deprecate).with(*expected_arguments) + o = Object.new + o2 = Object.new + o.should_not_receive(:bees) + o2.should_not_receive(:bees) + end + + it "warns about stubbing once, regardless of how many times it is called" do + expect(RSpec).to receive(:deprecate).with(*expected_arguments) + o = Object.new + o2 = Object.new + + o.stub(:faces) + o2.stub(:faces) + end + + it "warns about unstubbing once, regardless of how many times it is called" do + expect(RSpec).to receive(:deprecate).with(/Using.*without explicitly enabling/, + {:replacement => "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`"}) + o = Object.new + o2 = Object.new + + allow(o).to receive(:faces) + allow(o2).to receive(:faces) + + o.unstub(:faces) + o2.unstub(:faces) + end + + + it "doesn't warn about stubbing after a reset and setting should" do + expect(RSpec).not_to receive(:deprecate) + RSpec::Mocks.configuration.reset_syntaxes_to_default + RSpec::Mocks.configuration.syntax = :should + o = Object.new + o2 = Object.new + o.stub(:faces) + o2.stub(:faces) + end + + it "includes the call site in the deprecation warning" do + obj = Object.new + expect_deprecation_with_call_site(__FILE__, __LINE__ + 1) + obj.stub(:faces) + end +end + +RSpec.context "when the should syntax is enabled on a non-default syntax host" do + include_context "with the default mocks syntax" + + it "continues to warn about the should syntax" do + my_host = Class.new + expect(RSpec).to receive(:deprecate) + RSpec::Mocks::Syntax.enable_should(my_host) + + o = Object.new + o.should_receive(:bees) + o.bees + end +end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index d22f0d24b..5f28bea3e 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -303,10 +303,6 @@ def non_prepended_method }.to raise_error end - it "supports options" do - @stub.stub(:foo, :expected_from => "bar") - end - it 'uses the correct stubbed response when responding to a mock expectation' do @stub.stub(:bar) { 15 } @stub.stub(:bar).with(:eighteen) { 18 } diff --git a/spec/rspec/mocks/syntax_spec.rb b/spec/rspec/mocks/syntax_spec.rb deleted file mode 100644 index 51f1a238e..000000000 --- a/spec/rspec/mocks/syntax_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -module RSpec::Mocks - describe Syntax do - context "when the should syntax is enabled on a non-default syntax host" do - include_context "with the default mocks syntax" - - it "continues to warn about the should syntax" do - my_host = Class.new - expect(RSpec).to receive(:deprecate) - Syntax.enable_should(my_host) - - o = Object.new - o.should_receive(:bees) - o.bees - end - end - end -end From 411743175724d04885367f821e515824b747e781 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 26 Feb 2014 10:38:49 -0800 Subject: [PATCH 0521/1179] Convert specs to RSpec 3.0.0.beta2 syntax with Transpec This conversion is done by Transpec 1.9.3 with the following command: transpec -c "bundle install && bundle exec rspec" * 295 conversions from: obj.should_receive(:message) to: expect(obj).to receive(:message) * 128 conversions from: obj.stub(:message) to: allow(obj).to receive(:message) * 65 conversions from: Klass.any_instance.should_receive(:message) to: expect_any_instance_of(Klass).to receive(:message) * 50 conversions from: Klass.any_instance.stub(:message) to: allow_any_instance_of(Klass).to receive(:message) * 28 conversions from: obj.stub_chain(:message1, :message2) to: allow(obj).to receive_message_chain(:message1, :message2) * 24 conversions from: obj.should_not_receive(:message) to: expect(obj).not_to receive(:message) * 7 conversions from: Klass.any_instance.should_not_receive(:message) to: expect_any_instance_of(Klass).not_to receive(:message) * 7 conversions from: obj.stub(:message => value) to: allow(obj).to receive_messages(:message => value) * 3 conversions from: Klass.any_instance.stub_chain(:message1, :message2) to: allow_any_instance_of(Klass).to receive_message_chain(:message1, :message2) * 2 conversions from: Klass.any_instance.stub(:message => value) to: allow_any_instance_of(Klass).to receive_messages(:message => value) * 1 conversion from: obj.stub(:message).and_return to: obj.stub(:message) --- spec/rspec/mocks/and_call_original_spec.rb | 46 ++-- spec/rspec/mocks/and_return_spec.rb | 4 +- spec/rspec/mocks/and_yield_spec.rb | 32 +-- spec/rspec/mocks/any_instance_spec.rb | 258 +++++++++--------- spec/rspec/mocks/at_least_spec.rb | 32 +-- spec/rspec/mocks/at_most_spec.rb | 24 +- spec/rspec/mocks/before_all_spec.rb | 10 +- spec/rspec/mocks/block_return_value_spec.rb | 16 +- ...bining_implementation_instructions_spec.rb | 42 +-- spec/rspec/mocks/configuration_spec.rb | 4 +- spec/rspec/mocks/double_spec.rb | 194 ++++++------- .../mocks/failing_argument_matchers_spec.rb | 42 +-- .../mocks/matchers/have_received_spec.rb | 6 +- spec/rspec/mocks/mock_ordering_spec.rb | 64 ++--- .../rspec/mocks/multiple_return_value_spec.rb | 16 +- .../mocks/nil_expectation_warning_spec.rb | 20 +- spec/rspec/mocks/null_object_double_spec.rb | 18 +- spec/rspec/mocks/once_counts_spec.rb | 12 +- spec/rspec/mocks/partial_double_spec.rb | 42 +-- ...artial_double_using_mocks_directly_spec.rb | 14 +- .../mocks/passing_argument_matchers_spec.rb | 52 ++-- spec/rspec/mocks/precise_counts_spec.rb | 18 +- spec/rspec/mocks/serialization_spec.rb | 2 +- spec/rspec/mocks/space_spec.rb | 8 +- spec/rspec/mocks/stash_spec.rb | 6 +- spec/rspec/mocks/stub_chain_spec.rb | 52 ++-- spec/rspec/mocks/stub_implementation_spec.rb | 22 +- spec/rspec/mocks/stub_spec.rb | 72 ++--- .../stubbed_message_expectations_spec.rb | 18 +- spec/rspec/mocks/to_ary_spec.rb | 4 +- spec/rspec/mocks/twice_counts_spec.rb | 14 +- spec/rspec/mocks/verifying_double_spec.rb | 18 +- spec/rspec/mocks_spec.rb | 2 +- 33 files changed, 592 insertions(+), 592 deletions(-) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 66e1241a5..a923d60e9 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -48,8 +48,8 @@ def self.new_instance end it 'ignores prior declared stubs' do - instance.stub(:meth_1).and_return(:stubbed_value) - instance.should_receive(:meth_1).and_call_original + allow(instance).to receive(:meth_1).and_return(:stubbed_value) + expect(instance).to receive(:meth_1).and_call_original expect(instance.meth_1).to eq(:original) end @@ -76,28 +76,28 @@ def self.new_instance context "for singleton methods" do it 'works' do def instance.foo; :bar; end - instance.should_receive(:foo).and_call_original + expect(instance).to receive(:foo).and_call_original expect(instance.foo).to eq(:bar) end it 'works for SimpleDelegator subclasses', :if => (RUBY_VERSION.to_f > 1.8) do instance = Class.new(SimpleDelegator).new(1) def instance.foo; :bar; end - instance.should_receive(:foo).and_call_original + expect(instance).to receive(:foo).and_call_original expect(instance.foo).to eq(:bar) end end it 'works for methods added through an extended module' do instance.extend Module.new { def foo; :bar; end } - instance.should_receive(:foo).and_call_original + expect(instance).to receive(:foo).and_call_original expect(instance.foo).to eq(:bar) end it "works for method added through an extended module onto a class's ancestor" do sub_sub_klass = Class.new(Class.new(klass)) klass.extend Module.new { def foo; :bar; end } - sub_sub_klass.should_receive(:foo).and_call_original + expect(sub_sub_klass).to receive(:foo).and_call_original expect(sub_sub_klass.foo).to eq(:bar) end @@ -106,7 +106,7 @@ def instance.foo; :bar; end klass.extend Module.new { def foo; :klass_bar; end } sub_klass = Class.new(klass) sub_klass.extend Module.new { def foo; :sub_klass_bar; end } - sub_klass.should_receive(:foo).and_call_original + expect(sub_klass).to receive(:foo).and_call_original expect(sub_klass.foo).to eq(:sub_klass_bar) end @@ -115,49 +115,49 @@ def instance.foo; :bar; end klass.extend Module.new { def foo; :klass_bar; end } sub_klass = Class.new(klass) { def self.foo; :sub_klass_bar; end } sub_sub_klass = Class.new(sub_klass) - sub_sub_klass.should_receive(:foo).and_call_original + expect(sub_sub_klass).to receive(:foo).and_call_original expect(sub_sub_klass.foo).to eq(:sub_klass_bar) end context 'when using any_instance' do it 'works for instance methods defined on the class' do - klass.any_instance.should_receive(:meth_1).and_call_original + expect_any_instance_of(klass).to receive(:meth_1).and_call_original expect(klass.new.meth_1).to eq(:original) end it 'works for instance methods defined on the superclass of the class' do subclass = Class.new(klass) - subclass.any_instance.should_receive(:meth_1).and_call_original + expect_any_instance_of(subclass).to receive(:meth_1).and_call_original expect(subclass.new.meth_1).to eq(:original) end it 'works when mocking the method on one class and calling the method on an instance of a subclass' do - klass.any_instance.should_receive(:meth_1).and_call_original + expect_any_instance_of(klass).to receive(:meth_1).and_call_original expect(Class.new(klass).new.meth_1).to eq(:original) end end it 'works for class methods defined on a superclass' do subclass = Class.new(klass) - subclass.should_receive(:new_instance).and_call_original + expect(subclass).to receive(:new_instance).and_call_original expect(subclass.new_instance).to be_a(subclass) end it 'works for class methods defined on a grandparent class' do sub_subclass = Class.new(Class.new(klass)) - sub_subclass.should_receive(:new_instance).and_call_original + expect(sub_subclass).to receive(:new_instance).and_call_original expect(sub_subclass.new_instance).to be_a(sub_subclass) end it 'works for class methods defined on the Class class' do - klass.should_receive(:new).and_call_original + expect(klass).to receive(:new).and_call_original expect(klass.new).to be_an_instance_of(klass) end it "works for instance methods defined on the object's class's superclass" do subclass = Class.new(klass) inst = subclass.new - inst.should_receive(:meth_1).and_call_original + expect(inst).to receive(:meth_1).and_call_original expect(inst.meth_1).to eq(:original) end @@ -168,7 +168,7 @@ class << self end end - klass.should_receive(:alternate_new).and_call_original + expect(klass).to receive(:alternate_new).and_call_original expect(klass.alternate_new).to be_an_instance_of(klass) end @@ -188,24 +188,24 @@ def method_missing(name, *args) end it 'works when the method_missing definition handles the message' do - instance.should_receive(:greet_jack).and_call_original + expect(instance).to receive(:greet_jack).and_call_original expect(instance.greet_jack).to eq("Hello, jack") end it 'works for an any_instance partial mock' do - klass.any_instance.should_receive(:greet_jack).and_call_original + expect_any_instance_of(klass).to receive(:greet_jack).and_call_original expect(instance.greet_jack).to eq("Hello, jack") end it 'raises an error for an unhandled message for an any_instance partial mock' do - klass.any_instance.should_receive(:not_a_handled_message).and_call_original + expect_any_instance_of(klass).to receive(:not_a_handled_message).and_call_original expect { instance.not_a_handled_message }.to raise_error(NameError, /not_a_handled_message/) end it 'raises an error on invocation if method_missing does not handle the message' do - instance.should_receive(:not_a_handled_message).and_call_original + expect(instance).to receive(:not_a_handled_message).and_call_original # Note: it should raise a NoMethodError (and usually does), but # due to a weird rspec-expectations issue (see #183) it sometimes @@ -237,7 +237,7 @@ def self.method let(:request) { request_klass.new(:get, "http://foo.com/bar") } it 'still works even though #method has been overriden' do - request.should_receive(:perform).and_call_original + expect(request).to receive(:perform).and_call_original expect(request.perform).to eq(:the_response) end @@ -246,7 +246,7 @@ def request.perform :a_response end - request.should_receive(:perform).and_call_original + expect(request).to receive(:perform).and_call_original expect(request.perform).to eq(:a_response) end end @@ -256,7 +256,7 @@ def request.perform it 'raises an error even if the double object responds to the message' do expect(instance.to_s).to be_a(String) - mock_expectation = instance.should_receive(:to_s) + mock_expectation = expect(instance).to receive(:to_s) instance.to_s # to satisfy the expectation expect { diff --git a/spec/rspec/mocks/and_return_spec.rb b/spec/rspec/mocks/and_return_spec.rb index 25b7132ec..070ea10e0 100644 --- a/spec/rspec/mocks/and_return_spec.rb +++ b/spec/rspec/mocks/and_return_spec.rb @@ -6,14 +6,14 @@ module Mocks context 'when a block is passed' do it 'raises ArgumentError' do expect { - obj.stub(:foo).and_return('bar') { 'baz' } + allow(obj).to receive(:foo).and_return('bar') { 'baz' } }.to raise_error(ArgumentError, /implementation block/i) end end context 'when no argument is passed' do it 'raises ArgumentError' do - expect { obj.stub(:foo).and_return }.to raise_error(ArgumentError) + expect { allow(obj).to receive(:foo).and_return }.to raise_error(ArgumentError) end end end diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index f667dce25..52068254c 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -5,12 +5,12 @@ describe "#and_yield" do context 'when the method double has been constrained by `with`' do it 'uses the default stub if the provided args do not match' do - obj.stub(:foo) { 15 } - obj.stub(:foo).with(:yield).and_yield + allow(obj).to receive(:foo) { 15 } + allow(obj).to receive(:foo).with(:yield).and_yield # should_receive is required to trigger the bug: # https://github.com/rspec/rspec-mocks/issues/127 - obj.should_receive(:foo) + expect(obj).to receive(:foo) expect(obj.foo(:dont_yield)).to eq(15) end @@ -20,14 +20,14 @@ it "evaluates the supplied block as it is read" do evaluated = false - obj.stub(:method_that_accepts_a_block).and_yield do |eval_context| + allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| evaluated = true end expect(evaluated).to be_truthy end it "passes an eval context object to the supplied block" do - obj.stub(:method_that_accepts_a_block).and_yield do |eval_context| + allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| expect(eval_context).not_to be_nil end end @@ -36,7 +36,7 @@ expected_eval_context = nil actual_eval_context = nil - obj.stub(:method_that_accepts_a_block).and_yield do |eval_context| + allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| expected_eval_context = eval_context end @@ -51,9 +51,9 @@ it "passes when expectations set on the eval context are met" do configured_eval_context = nil - obj.stub(:method_that_accepts_a_block).and_yield do |eval_context| + allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| configured_eval_context = eval_context - configured_eval_context.should_receive(:foo) + expect(configured_eval_context).to receive(:foo) end obj.method_that_accepts_a_block do @@ -65,9 +65,9 @@ it "fails when expectations set on the eval context are not met" do configured_eval_context = nil - obj.stub(:method_that_accepts_a_block).and_yield do |eval_context| + allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| configured_eval_context = eval_context - configured_eval_context.should_receive(:foo) + expect(configured_eval_context).to receive(:foo) end obj.method_that_accepts_a_block do @@ -84,10 +84,10 @@ it "passes when expectations set on the eval context and yielded arguments are met" do configured_eval_context = nil yielded_arg = Object.new - obj.stub(:method_that_accepts_a_block).and_yield(yielded_arg) do |eval_context| + allow(obj).to receive(:method_that_accepts_a_block).and_yield(yielded_arg) do |eval_context| configured_eval_context = eval_context - configured_eval_context.should_receive(:foo) - yielded_arg.should_receive(:bar) + expect(configured_eval_context).to receive(:foo) + expect(yielded_arg).to receive(:bar) end obj.method_that_accepts_a_block do |obj| @@ -102,10 +102,10 @@ it "fails when expectations set on the eval context and yielded arguments are not met" do configured_eval_context = nil yielded_arg = Object.new - obj.stub(:method_that_accepts_a_block).and_yield(yielded_arg) do |eval_context| + allow(obj).to receive(:method_that_accepts_a_block).and_yield(yielded_arg) do |eval_context| configured_eval_context = eval_context - configured_eval_context.should_receive(:foo) - yielded_arg.should_receive(:bar) + expect(configured_eval_context).to receive(:foo) + expect(yielded_arg).to receive(:bar) end obj.method_that_accepts_a_block do |obj| diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 318f82f3b..07c5b2dfd 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -19,77 +19,77 @@ def private_method; :private_method_return_value; end context "invocation order" do context "#stub" do it "raises an error if 'stub' follows 'with'" do - expect { klass.any_instance.with("1").stub(:foo) }.to raise_error(NoMethodError) + expect { allow(klass.any_instance.with("1")).to receive(:foo) }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_return'" do - expect { klass.any_instance.stub(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) + expect { allow_any_instance_of(klass).to receive(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_raise'" do - expect { klass.any_instance.stub(:foo).and_raise(1).with("1") }.to raise_error(NoMethodError) + expect { allow_any_instance_of(klass).to receive(:foo).and_raise(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_yield'" do - expect { klass.any_instance.stub(:foo).and_yield(1).with("1") }.to raise_error(NoMethodError) + expect { allow_any_instance_of(klass).to receive(:foo).and_yield(1).with("1") }.to raise_error(NoMethodError) end end context "#stub_chain" do it "raises an error if 'stub_chain' follows 'and_return'" do - expect { klass.any_instance.and_return("1").stub_chain(:foo, :bar) }.to raise_error(NoMethodError) + expect { allow(klass.any_instance.and_return("1")).to receive_message_chain(:foo, :bar) }.to raise_error(NoMethodError) end end context "#should_receive" do it "raises an error if 'should_receive' follows 'with'" do - expect { klass.any_instance.with("1").should_receive(:foo) }.to raise_error(NoMethodError) + expect { expect(klass.any_instance.with("1")).to receive(:foo) }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_return'" do pending "see Github issue #42" - expect { klass.any_instance.should_receive(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) + expect { expect_any_instance_of(klass).to receive(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_raise'" do pending "see Github issue #42" - expect { klass.any_instance.should_receive(:foo).and_raise(1).with("1") }.to raise_error(NoMethodError) + expect { expect_any_instance_of(klass).to receive(:foo).and_raise(1).with("1") }.to raise_error(NoMethodError) end end end context "with #stub" do it "does not suppress an exception when a method that doesn't exist is invoked" do - klass.any_instance.stub(:foo) + allow_any_instance_of(klass).to receive(:foo) expect { klass.new.bar }.to raise_error(NoMethodError) end context 'multiple methods' do it "allows multiple methods to be stubbed in a single invocation" do - klass.any_instance.stub(:foo => 'foo', :bar => 'bar') + allow_any_instance_of(klass).to receive_messages(:foo => 'foo', :bar => 'bar') instance = klass.new expect(instance.foo).to eq('foo') expect(instance.bar).to eq('bar') end it "adheres to the contract of multiple method stubbing withou any instance" do - expect(Object.new.stub(:foo => 'foo', :bar => 'bar')).to eq(:foo => 'foo', :bar => 'bar') - expect(klass.any_instance.stub(:foo => 'foo', :bar => 'bar')).to eq(:foo => 'foo', :bar => 'bar') + expect(allow(Object.new).to receive_messages(:foo => 'foo', :bar => 'bar')).to eq(:foo => 'foo', :bar => 'bar') + expect(allow_any_instance_of(klass).to receive_messages(:foo => 'foo', :bar => 'bar')).to eq(:foo => 'foo', :bar => 'bar') end context "allows a chain of methods to be stubbed using #stub_chain" do it "given symbols representing the methods" do - klass.any_instance.stub_chain(:one, :two, :three).and_return(:four) + allow_any_instance_of(klass).to receive_message_chain(:one, :two, :three).and_return(:four) expect(klass.new.one.two.three).to eq(:four) end it "given a hash as the last argument uses the value as the expected return value" do - klass.any_instance.stub_chain(:one, :two, :three => :four) + allow_any_instance_of(klass).to receive_message_chain(:one, :two, :three => :four) expect(klass.new.one.two.three).to eq(:four) end it "given a string of '.' separated method names representing the chain" do - klass.any_instance.stub_chain('one.two.three').and_return(:four) + allow_any_instance_of(klass).to receive_message_chain('one.two.three').and_return(:four) expect(klass.new.one.two.three).to eq(:four) end end @@ -100,33 +100,33 @@ def private_method; :private_method_return_value; end let(:sub_class) { Class.new(super_class) } it "stubs every instance in the spec" do - klass.any_instance.stub(:foo).and_return(result = Object.new) + allow_any_instance_of(klass).to receive(:foo).and_return(result = Object.new) expect(klass.new.foo).to eq(result) expect(klass.new.foo).to eq(result) end it "stubs instance created before any_instance was called" do instance = klass.new - klass.any_instance.stub(:foo).and_return(result = Object.new) + allow_any_instance_of(klass).to receive(:foo).and_return(result = Object.new) expect(instance.foo).to eq(result) end it 'handles freeze and duplication correctly' do - String.any_instance.stub(:any_method) + allow_any_instance_of(String).to receive(:any_method) foo = 'foo'.freeze expect(foo.dup.concat 'bar').to eq 'foobar' end it 'handles stubbing on super and subclasses' do - super_class.any_instance.stub(:foo) - sub_class.any_instance.stub(:foo).and_return('baz') + allow_any_instance_of(super_class).to receive(:foo) + allow_any_instance_of(sub_class).to receive(:foo).and_return('baz') expect(sub_class.new.foo).to eq('baz') end it 'handles method restoration on subclasses' do - super_class.any_instance.stub(:foo) - sub_class.any_instance.stub(:foo) + allow_any_instance_of(super_class).to receive(:foo) + allow_any_instance_of(sub_class).to receive(:foo) sub_class.any_instance.unstub(:foo) expect(sub_class.new.foo).to eq("bar") end @@ -134,8 +134,8 @@ def private_method; :private_method_return_value; end context "with argument matching" do before do - klass.any_instance.stub(:foo).with(:param_one, :param_two).and_return(:result_one) - klass.any_instance.stub(:foo).with(:param_three, :param_four).and_return(:result_two) + allow_any_instance_of(klass).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) + allow_any_instance_of(klass).to receive(:foo).with(:param_three, :param_four).and_return(:result_two) end it "returns the stubbed value when arguments match" do @@ -153,8 +153,8 @@ def private_method; :private_method_return_value; end context "with multiple stubs" do before do - klass.any_instance.stub(:foo).and_return(1) - klass.any_instance.stub(:bar).and_return(2) + allow_any_instance_of(klass).to receive(:foo).and_return(1) + allow_any_instance_of(klass).to receive(:bar).and_return(2) end it "stubs a method" do @@ -171,18 +171,18 @@ def private_method; :private_method_return_value; end context "with #and_return" do it "stubs a method that doesn't exist" do - klass.any_instance.stub(:foo).and_return(1) + allow_any_instance_of(klass).to receive(:foo).and_return(1) expect(klass.new.foo).to eq(1) end it "stubs a method that exists" do - klass.any_instance.stub(:existing_method).and_return(1) + allow_any_instance_of(klass).to receive(:existing_method).and_return(1) expect(klass.new.existing_method).to eq(1) end it "returns the same object for calls on different instances" do return_value = Object.new - klass.any_instance.stub(:foo).and_return(return_value) + allow_any_instance_of(klass).to receive(:foo).and_return(return_value) expect(klass.new.foo).to be(return_value) expect(klass.new.foo).to be(return_value) end @@ -191,7 +191,7 @@ def private_method; :private_method_return_value; end context "with #and_yield" do it "yields the value specified" do yielded_value = Object.new - klass.any_instance.stub(:foo).and_yield(yielded_value) + allow_any_instance_of(klass).to receive(:foo).and_yield(yielded_value) klass.new.foo{|value| expect(value).to be(yielded_value)} end end @@ -208,8 +208,8 @@ def private_method; :private_method_return_value; end end it 'can combine and_call_original, with, and_return (old syntax)' do - klass.any_instance.stub(:a_method).and_call_original - klass.any_instance.stub(:a_method).with(:arg).and_return('value') + allow_any_instance_of(klass).to receive(:a_method).and_call_original + allow_any_instance_of(klass).to receive(:a_method).with(:arg).and_return('value') expect(klass.new('org').a_method).to eq 'org' expect(klass.new.a_method(:arg)).to eq 'value' @@ -218,24 +218,24 @@ def private_method; :private_method_return_value; end context "with #and_raise" do it "stubs a method that doesn't exist" do - klass.any_instance.stub(:foo).and_raise(CustomErrorForAnyInstanceSpec) + allow_any_instance_of(klass).to receive(:foo).and_raise(CustomErrorForAnyInstanceSpec) expect { klass.new.foo}.to raise_error(CustomErrorForAnyInstanceSpec) end it "stubs a method that exists" do - klass.any_instance.stub(:existing_method).and_raise(CustomErrorForAnyInstanceSpec) + allow_any_instance_of(klass).to receive(:existing_method).and_raise(CustomErrorForAnyInstanceSpec) expect { klass.new.existing_method}.to raise_error(CustomErrorForAnyInstanceSpec) end end context "with a block" do it "stubs a method" do - klass.any_instance.stub(:foo) { 1 } + allow_any_instance_of(klass).to receive(:foo) { 1 } expect(klass.new.foo).to eq(1) end it "returns the same computed value for calls on different instances" do - klass.any_instance.stub(:foo) { 1 + 2 } + allow_any_instance_of(klass).to receive(:foo) { 1 + 2 } expect(klass.new.foo).to eq(klass.new.foo) end end @@ -260,38 +260,38 @@ def private_method; :private_method_return_value; end context "core ruby objects" do it "works uniformly across *everything*" do - Object.any_instance.stub(:foo).and_return(1) + allow_any_instance_of(Object).to receive(:foo).and_return(1) expect(Object.new.foo).to eq(1) end it "works with the non-standard constructor []" do - Array.any_instance.stub(:foo).and_return(1) + allow_any_instance_of(Array).to receive(:foo).and_return(1) expect([].foo).to eq(1) end it "works with the non-standard constructor {}" do - Hash.any_instance.stub(:foo).and_return(1) + allow_any_instance_of(Hash).to receive(:foo).and_return(1) expect({}.foo).to eq(1) end it "works with the non-standard constructor \"\"" do - String.any_instance.stub(:foo).and_return(1) + allow_any_instance_of(String).to receive(:foo).and_return(1) expect("".foo).to eq(1) end it "works with the non-standard constructor \'\'" do - String.any_instance.stub(:foo).and_return(1) + allow_any_instance_of(String).to receive(:foo).and_return(1) expect(''.foo).to eq(1) end it "works with the non-standard constructor module" do - Module.any_instance.stub(:foo).and_return(1) + allow_any_instance_of(Module).to receive(:foo).and_return(1) module RSpec::SampleRspecTestModule;end expect(RSpec::SampleRspecTestModule.foo).to eq(1) end it "works with the non-standard constructor class" do - Class.any_instance.stub(:foo).and_return(1) + allow_any_instance_of(Class).to receive(:foo).and_return(1) class RSpec::SampleRspecTestClass;end expect(RSpec::SampleRspecTestClass.foo).to eq(1) end @@ -300,20 +300,20 @@ class RSpec::SampleRspecTestClass;end context "unstub implementation" do it "replaces the stubbed method with the original method" do - klass.any_instance.stub(:existing_method) + allow_any_instance_of(klass).to receive(:existing_method) klass.any_instance.unstub(:existing_method) expect(klass.new.existing_method).to eq(:existing_method_return_value) end it "removes all stubs with the supplied method name" do - klass.any_instance.stub(:existing_method).with(1) - klass.any_instance.stub(:existing_method).with(2) + allow_any_instance_of(klass).to receive(:existing_method).with(1) + allow_any_instance_of(klass).to receive(:existing_method).with(2) klass.any_instance.unstub(:existing_method) expect(klass.new.existing_method).to eq(:existing_method_return_value) end it "removes stubs even if they have already been invoked" do - klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = klass.new obj.existing_method klass.any_instance.unstub(:existing_method) @@ -321,7 +321,7 @@ class RSpec::SampleRspecTestClass;end end it "removes stubs from sub class after invokation when super class was originally stubbed" do - klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = Class.new(klass).new expect(obj.existing_method).to eq(:any_instance_value) klass.any_instance.unstub(:existing_method) @@ -329,17 +329,17 @@ class RSpec::SampleRspecTestClass;end end it "does not remove any stubs set directly on an instance" do - klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = klass.new - obj.stub(:existing_method).and_return(:local_method) + allow(obj).to receive(:existing_method).and_return(:local_method) klass.any_instance.unstub(:existing_method) expect(obj.existing_method).to eq(:local_method) end it "does not remove any expectations with the same method name" do - klass.any_instance.should_receive(:existing_method_with_arguments).with(3).and_return(:three) - klass.any_instance.stub(:existing_method_with_arguments).with(1) - klass.any_instance.stub(:existing_method_with_arguments).with(2) + expect_any_instance_of(klass).to receive(:existing_method_with_arguments).with(3).and_return(:three) + allow_any_instance_of(klass).to receive(:existing_method_with_arguments).with(1) + allow_any_instance_of(klass).to receive(:existing_method_with_arguments).with(2) klass.any_instance.unstub(:existing_method_with_arguments) expect(klass.new.existing_method_with_arguments(3)).to eq(:three) end @@ -351,7 +351,7 @@ class RSpec::SampleRspecTestClass;end end it 'does not get confused about string vs symbol usage for the message' do - klass.any_instance.stub(:existing_method) { :stubbed } + allow_any_instance_of(klass).to receive(:existing_method) { :stubbed } klass.any_instance.unstub("existing_method") expect(klass.new.existing_method).to eq(:existing_method_return_value) end @@ -359,37 +359,37 @@ class RSpec::SampleRspecTestClass;end context "with #should_not_receive" do it "fails if the method is called" do - klass.any_instance.should_not_receive(:existing_method) + expect_any_instance_of(klass).not_to receive(:existing_method) expect { klass.new.existing_method }.to raise_error(RSpec::Mocks::MockExpectationError) end it "passes if no method is called" do - expect { klass.any_instance.should_not_receive(:existing_method) }.to_not raise_error + expect { expect_any_instance_of(klass).not_to receive(:existing_method) }.to_not raise_error end it "passes if only a different method is called" do - klass.any_instance.should_not_receive(:existing_method) + expect_any_instance_of(klass).not_to receive(:existing_method) expect { klass.new.another_existing_method }.to_not raise_error end context "with constraints" do it "fails if the method is called with the specified parameters" do - klass.any_instance.should_not_receive(:existing_method_with_arguments).with(:argument_one, :argument_two) + expect_any_instance_of(klass).not_to receive(:existing_method_with_arguments).with(:argument_one, :argument_two) expect { klass.new.existing_method_with_arguments(:argument_one, :argument_two) }.to raise_error(RSpec::Mocks::MockExpectationError) end it "passes if the method is called with different parameters" do - klass.any_instance.should_not_receive(:existing_method_with_arguments).with(:argument_one, :argument_two) + expect_any_instance_of(klass).not_to receive(:existing_method_with_arguments).with(:argument_one, :argument_two) expect { klass.new.existing_method_with_arguments(:argument_three, :argument_four) }.to_not raise_error end end context 'when used in combination with should_receive' do it 'passes if only the expected message is received' do - klass.any_instance.should_receive(:foo) - klass.any_instance.should_not_receive(:bar) + expect_any_instance_of(klass).to receive(:foo) + expect_any_instance_of(klass).not_to receive(:bar) klass.new.foo verify_all end @@ -397,7 +397,7 @@ class RSpec::SampleRspecTestClass;end it "prevents confusing double-negative expressions involving `never`" do expect { - klass.any_instance.should_not_receive(:not_expected).never + expect_any_instance_of(klass).not_to receive(:not_expected).never }.to raise_error(/trying to negate it again/) end end @@ -408,13 +408,13 @@ class RSpec::SampleRspecTestClass;end context "with an expectation is set on a method which does not exist" do it "returns the expected value" do - klass.any_instance.should_receive(:foo).and_return(1) + expect_any_instance_of(klass).to receive(:foo).and_return(1) expect(klass.new.foo(1)).to eq(1) end it "fails if an instance is created but no invocation occurs" do expect do - klass.any_instance.should_receive(:foo) + expect_any_instance_of(klass).to receive(:foo) klass.new verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) @@ -422,31 +422,31 @@ class RSpec::SampleRspecTestClass;end it "fails if no instance is created" do expect do - klass.any_instance.should_receive(:foo).and_return(1) + expect_any_instance_of(klass).to receive(:foo).and_return(1) verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) end it "fails if no instance is created and there are multiple expectations" do expect do - klass.any_instance.should_receive(:foo) - klass.any_instance.should_receive(:bar) + expect_any_instance_of(klass).to receive(:foo) + expect_any_instance_of(klass).to receive(:bar) verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, 'Exactly one instance should have received the following message(s) but didn\'t: bar, foo') end it "allows expectations on instances to take priority" do - klass.any_instance.should_receive(:foo) + expect_any_instance_of(klass).to receive(:foo) klass.new.foo instance = klass.new - instance.should_receive(:foo).and_return(result = Object.new) + expect(instance).to receive(:foo).and_return(result = Object.new) expect(instance.foo).to eq(result) end context "behaves as 'exactly one instance'" do it "passes if subsequent invocations do not receive that message" do - klass.any_instance.should_receive(:foo) + expect_any_instance_of(klass).to receive(:foo) klass.new.foo klass.new end @@ -455,7 +455,7 @@ class RSpec::SampleRspecTestClass;end instance_one = klass.new instance_two = klass.new expect do - klass.any_instance.should_receive(:foo) + expect_any_instance_of(klass).to receive(:foo) instance_one.foo instance_two.foo @@ -466,8 +466,8 @@ class RSpec::SampleRspecTestClass;end context "normal expectations on the class object" do it "fail when unfulfilled" do expect do - klass.any_instance.should_receive(:foo) - klass.should_receive(:woot) + expect_any_instance_of(klass).to receive(:foo) + expect(klass).to receive(:woot) klass.new.foo verify_all end.to(raise_error(RSpec::Mocks::MockExpectationError) do |error| @@ -477,8 +477,8 @@ class RSpec::SampleRspecTestClass;end it "pass when expectations are met" do - klass.any_instance.should_receive(:foo) - klass.should_receive(:woot).and_return(result = Object.new) + expect_any_instance_of(klass).to receive(:foo) + expect(klass).to receive(:woot).and_return(result = Object.new) klass.new.foo expect(klass.woot).to eq(result) end @@ -487,13 +487,13 @@ class RSpec::SampleRspecTestClass;end context "with an expectation is set on a method that exists" do it "returns the expected value" do - klass.any_instance.should_receive(:existing_method).and_return(1) + expect_any_instance_of(klass).to receive(:existing_method).and_return(1) expect(klass.new.existing_method(1)).to eq(1) end it "fails if an instance is created but no invocation occurs" do expect do - klass.any_instance.should_receive(:existing_method) + expect_any_instance_of(klass).to receive(:existing_method) klass.new verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) @@ -501,22 +501,22 @@ class RSpec::SampleRspecTestClass;end it "fails if no instance is created" do expect do - klass.any_instance.should_receive(:existing_method) + expect_any_instance_of(klass).to receive(:existing_method) verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) end it "fails if no instance is created and there are multiple expectations" do expect do - klass.any_instance.should_receive(:existing_method) - klass.any_instance.should_receive(:another_existing_method) + expect_any_instance_of(klass).to receive(:existing_method) + expect_any_instance_of(klass).to receive(:another_existing_method) verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, 'Exactly one instance should have received the following message(s) but didn\'t: another_existing_method, existing_method') end context "after any one instance has received a message" do it "passes if subsequent invocations do not receive that message" do - klass.any_instance.should_receive(:existing_method) + expect_any_instance_of(klass).to receive(:existing_method) klass.new.existing_method klass.new end @@ -525,7 +525,7 @@ class RSpec::SampleRspecTestClass;end instance_one = klass.new instance_two = klass.new expect do - klass.any_instance.should_receive(:existing_method) + expect_any_instance_of(klass).to receive(:existing_method) instance_one.existing_method instance_two.existing_method @@ -540,7 +540,7 @@ class RSpec::SampleRspecTestClass;end def foo; end end - klass.any_instance.should_receive(:foo) + expect_any_instance_of(klass).to receive(:foo) klass.new.foo end @@ -549,14 +549,14 @@ def foo; end def foo; end end - klass.any_instance.should_receive(:foo) + expect_any_instance_of(klass).to receive(:foo) klass.new(Object.new).foo end context "with argument matching" do before do - klass.any_instance.should_receive(:foo).with(:param_one, :param_two).and_return(:result_one) - klass.any_instance.should_receive(:foo).with(:param_three, :param_four).and_return(:result_two) + expect_any_instance_of(klass).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) + expect_any_instance_of(klass).to receive(:foo).with(:param_three, :param_four).and_return(:result_two) end it "returns the expected value when arguments match" do @@ -597,20 +597,20 @@ def foo; end context "message count" do context "the 'once' constraint" do it "passes for one invocation" do - klass.any_instance.should_receive(:foo).once + expect_any_instance_of(klass).to receive(:foo).once klass.new.foo end it "fails when no instances are declared" do expect do - klass.any_instance.should_receive(:foo).once + expect_any_instance_of(klass).to receive(:foo).once verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) end it "fails when an instance is declared but there are no invocations" do expect do - klass.any_instance.should_receive(:foo).once + expect_any_instance_of(klass).to receive(:foo).once klass.new verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) @@ -618,7 +618,7 @@ def foo; end it "fails for more than one invocation" do expect do - klass.any_instance.should_receive(:foo).once + expect_any_instance_of(klass).to receive(:foo).once instance = klass.new 2.times { instance.foo } verify instance @@ -628,14 +628,14 @@ def foo; end context "the 'twice' constraint" do it "passes for two invocations" do - klass.any_instance.should_receive(:foo).twice + expect_any_instance_of(klass).to receive(:foo).twice instance = klass.new 2.times { instance.foo } end it "fails for more than two invocations" do expect do - klass.any_instance.should_receive(:foo).twice + expect_any_instance_of(klass).to receive(:foo).twice instance = klass.new 3.times { instance.foo } verify instance @@ -645,14 +645,14 @@ def foo; end context "the 'exactly(n)' constraint" do it "passes for n invocations where n = 3" do - klass.any_instance.should_receive(:foo).exactly(3).times + expect_any_instance_of(klass).to receive(:foo).exactly(3).times instance = klass.new 3.times { instance.foo } end it "fails for n invocations where n < 3" do expect do - klass.any_instance.should_receive(:foo).exactly(3).times + expect_any_instance_of(klass).to receive(:foo).exactly(3).times instance = klass.new 2.times { instance.foo } verify instance @@ -661,7 +661,7 @@ def foo; end it "fails for n invocations where n > 3" do expect do - klass.any_instance.should_receive(:foo).exactly(3).times + expect_any_instance_of(klass).to receive(:foo).exactly(3).times instance = klass.new 4.times { instance.foo } verify instance @@ -671,14 +671,14 @@ def foo; end context "the 'at_least(n)' constraint" do it "passes for n invocations where n = 3" do - klass.any_instance.should_receive(:foo).at_least(3).times + expect_any_instance_of(klass).to receive(:foo).at_least(3).times instance = klass.new 3.times { instance.foo } end it "fails for n invocations where n < 3" do expect do - klass.any_instance.should_receive(:foo).at_least(3).times + expect_any_instance_of(klass).to receive(:foo).at_least(3).times instance = klass.new 2.times { instance.foo } verify instance @@ -686,7 +686,7 @@ def foo; end end it "passes for n invocations where n > 3" do - klass.any_instance.should_receive(:foo).at_least(3).times + expect_any_instance_of(klass).to receive(:foo).at_least(3).times instance = klass.new 4.times { instance.foo } end @@ -694,20 +694,20 @@ def foo; end context "the 'at_most(n)' constraint" do it "passes for n invocations where n = 3" do - klass.any_instance.should_receive(:foo).at_most(3).times + expect_any_instance_of(klass).to receive(:foo).at_most(3).times instance = klass.new 3.times { instance.foo } end it "passes for n invocations where n < 3" do - klass.any_instance.should_receive(:foo).at_most(3).times + expect_any_instance_of(klass).to receive(:foo).at_most(3).times instance = klass.new 2.times { instance.foo } end it "fails for n invocations where n > 3" do expect do - klass.any_instance.should_receive(:foo).at_most(3).times + expect_any_instance_of(klass).to receive(:foo).at_most(3).times instance = klass.new 4.times { instance.foo } verify instance @@ -717,28 +717,28 @@ def foo; end context "the 'never' constraint" do it "passes for 0 invocations" do - klass.any_instance.should_receive(:foo).never + expect_any_instance_of(klass).to receive(:foo).never verify_all end it "fails on the first invocation" do expect do - klass.any_instance.should_receive(:foo).never + expect_any_instance_of(klass).to receive(:foo).never klass.new.foo end.to raise_error(RSpec::Mocks::MockExpectationError) end context "when combined with other expectations" do it "passes when the other expecations are met" do - klass.any_instance.should_receive(:foo).never - klass.any_instance.should_receive(:existing_method).and_return(5) + expect_any_instance_of(klass).to receive(:foo).never + expect_any_instance_of(klass).to receive(:existing_method).and_return(5) expect(klass.new.existing_method).to eq(5) end it "fails when the other expecations are not met" do expect do - klass.any_instance.should_receive(:foo).never - klass.any_instance.should_receive(:existing_method).and_return(5) + expect_any_instance_of(klass).to receive(:foo).never + expect_any_instance_of(klass).to receive(:existing_method).and_return(5) verify_all end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) end @@ -758,7 +758,7 @@ def foo; end context "with stubbing" do context "public methods" do before(:each) do - klass.any_instance.stub(:existing_method).and_return(1) + allow_any_instance_of(klass).to receive(:existing_method).and_return(1) expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_truthy end @@ -791,7 +791,7 @@ def foo; end context "private methods" do before :each do - klass.any_instance.stub(:private_method).and_return(:something) + allow_any_instance_of(klass).to receive(:private_method).and_return(:something) verify_all end @@ -813,7 +813,7 @@ def foo; end context "with expectations" do context "private methods" do before :each do - klass.any_instance.should_receive(:private_method).and_return(:something) + expect_any_instance_of(klass).to receive(:private_method).and_return(:something) klass.new.private_method verify_all @@ -835,7 +835,7 @@ def foo; end context "ensures that the subsequent specs do not see expectations set in previous specs" do context "when the instance created after the expectation is set" do it "first spec" do - klass.any_instance.should_receive(:existing_method).and_return(Object.new) + expect_any_instance_of(klass).to receive(:existing_method).and_return(Object.new) klass.new.existing_method end @@ -850,7 +850,7 @@ def foo; end end it "first spec" do - klass.any_instance.should_receive(:existing_method).and_return(Object.new) + expect_any_instance_of(klass).to receive(:existing_method).and_return(Object.new) @instance.existing_method end @@ -861,7 +861,7 @@ def foo; end end it "ensures that the next spec does not see that expectation" do - klass.any_instance.should_receive(:existing_method).and_return(Object.new) + expect_any_instance_of(klass).to receive(:existing_method).and_return(Object.new) klass.new.existing_method verify_all @@ -873,8 +873,8 @@ def foo; end context "with multiple calls to any_instance in the same example" do it "does not prevent the change from being rolled back" do - klass.any_instance.stub(:existing_method).and_return(false) - klass.any_instance.stub(:existing_method).and_return(true) + allow_any_instance_of(klass).to receive(:existing_method).and_return(false) + allow_any_instance_of(klass).to receive(:existing_method).and_return(true) verify_all expect(klass.new).to respond_to(:existing_method) @@ -889,7 +889,7 @@ def foo; end end it "adds an instance to the current space when stubbed method is invoked" do - klass.any_instance.stub(:foo) + allow_any_instance_of(klass).to receive(:foo) instance = klass.new instance.foo expect(RSpec::Mocks.space.proxies.keys).to include(instance.object_id) @@ -908,7 +908,7 @@ def foo; end instance = klass.new expect { |b| - klass.any_instance.should_receive(:bees).with(:sup, &b) + expect_any_instance_of(klass).to receive(:bees).with(:sup, &b) instance.bees(:sup) }.to yield_with_args(instance, :sup) end @@ -919,7 +919,7 @@ def call(*args) args.first end end - klass.any_instance.should_receive(:call).and_call_original + expect_any_instance_of(klass).to receive(:call).and_call_original instance = klass.new expect(instance.call(:bees)).to be :bees end @@ -928,7 +928,7 @@ def call(*args) describe "an any instance expectation" do it "doesn't effect with" do instance = klass.new - klass.any_instance.should_receive(:bees).with(:sup) + expect_any_instance_of(klass).to receive(:bees).with(:sup) instance.bees(:sup) end @@ -936,7 +936,7 @@ def call(*args) instance = klass.new expect { |b| - klass.any_instance.should_receive(:bees).with(:sup, &b) + expect_any_instance_of(klass).to receive(:bees).with(:sup, &b) instance.bees(:sup) }.to yield_with_args(instance, :sup) end @@ -954,14 +954,14 @@ def call(*args) instance = klass.new expect { |b| - klass.any_instance.should_receive(:bees).with(:sup, &b) + expect_any_instance_of(klass).to receive(:bees).with(:sup, &b) instance.bees(:sup) }.to yield_with_args(:sup) end it "does not cause with to fail when the instance is passed" do instance = klass.new - klass.any_instance.should_receive(:bees).with(:faces) + expect_any_instance_of(klass).to receive(:bees).with(:faces) instance.bees(:faces) end end @@ -972,7 +972,7 @@ def call(*args) it "doesn't cause an infinite loop" do skip "This intermittently fails on JRuby" if RUBY_PLATFORM == 'java' - Object.any_instance.stub(:some_method) + allow_any_instance_of(Object).to receive(:some_method) o = Object.new o.some_method expect { o.dup.some_method }.to_not raise_error @@ -1001,7 +1001,7 @@ def dup(funky_option) let(:sub_klass) { Class.new(klass) } it "stubs the method correctly" do - klass.any_instance.stub(:existing_method).and_return("foo") + allow_any_instance_of(klass).to receive(:existing_method).and_return("foo") expect(sub_klass.new.existing_method).to eq "foo" end @@ -1009,7 +1009,7 @@ def dup(funky_option) instance_one = sub_klass.new instance_two = sub_klass.new expect do - klass.any_instance.should_receive(:existing_method) + expect_any_instance_of(klass).to receive(:existing_method) instance_one.existing_method instance_two.existing_method end.to raise_error(RSpec::Mocks::MockExpectationError, "The message 'existing_method' was received by #{instance_two.inspect} but has already been received by #{instance_one.inspect}") @@ -1020,19 +1020,19 @@ def dup(funky_option) let(:http_request_class) { Struct.new(:method, :uri) } it "stubs the method correctly" do - http_request_class.any_instance.stub(:existing_method).and_return("foo") + allow_any_instance_of(http_request_class).to receive(:existing_method).and_return("foo") expect(http_request_class.new.existing_method).to eq "foo" end it "mocks the method correctly" do - http_request_class.any_instance.should_receive(:existing_method).and_return("foo") + expect_any_instance_of(http_request_class).to receive(:existing_method).and_return("foo") expect(http_request_class.new.existing_method).to eq "foo" end end context "when used after the test has finished" do it "restores the original behavior of a stubbed method" do - klass.any_instance.stub(:existing_method).and_return(:stubbed_return_value) + allow_any_instance_of(klass).to receive(:existing_method).and_return(:stubbed_return_value) instance = klass.new expect(instance.existing_method).to eq :stubbed_return_value @@ -1043,7 +1043,7 @@ def dup(funky_option) end it "restores the original behaviour, even if the expectation fails" do - klass.any_instance.should_receive(:existing_method).with(1).and_return(:stubbed_return_value) + expect_any_instance_of(klass).to receive(:existing_method).with(1).and_return(:stubbed_return_value) instance = klass.new begin diff --git a/spec/rspec/mocks/at_least_spec.rb b/spec/rspec/mocks/at_least_spec.rb index 574dc6043..12068d224 100644 --- a/spec/rspec/mocks/at_least_spec.rb +++ b/spec/rspec/mocks/at_least_spec.rb @@ -4,14 +4,14 @@ module Mocks before(:each) { @double = double } it "fails if method is never called" do - @double.should_receive(:do_something).at_least(4).times + expect(@double).to receive(:do_something).at_least(4).times expect { verify @double }.to raise_error(/expected: at least 4 times.*received: 0 times/m) end it "fails when called less than n times" do - @double.should_receive(:do_something).at_least(4).times + expect(@double).to receive(:do_something).at_least(4).times @double.do_something @double.do_something @double.do_something @@ -21,14 +21,14 @@ module Mocks end it "fails when at least once method is never called" do - @double.should_receive(:do_something).at_least(:once) + expect(@double).to receive(:do_something).at_least(:once) expect { verify @double }.to raise_error(/expected: at least 1 time.*received: 0 times/m) end it "fails when at least twice method is called once" do - @double.should_receive(:do_something).at_least(:twice) + expect(@double).to receive(:do_something).at_least(:twice) @double.do_something expect { verify @double @@ -36,14 +36,14 @@ module Mocks end it "fails when at least twice method is never called" do - @double.should_receive(:do_something).at_least(:twice) + expect(@double).to receive(:do_something).at_least(:twice) expect { verify @double }.to raise_error(/expected: at least 2 times.*received: 0 times/m) end it "passes when at least n times method is called exactly n times" do - @double.should_receive(:do_something).at_least(4).times + expect(@double).to receive(:do_something).at_least(4).times @double.do_something @double.do_something @double.do_something @@ -52,7 +52,7 @@ module Mocks end it "passes when at least n times method is called n plus 1 times" do - @double.should_receive(:do_something).at_least(4).times + expect(@double).to receive(:do_something).at_least(4).times @double.do_something @double.do_something @double.do_something @@ -62,20 +62,20 @@ module Mocks end it "passes when at least once method is called once" do - @double.should_receive(:do_something).at_least(:once) + expect(@double).to receive(:do_something).at_least(:once) @double.do_something verify @double end it "passes when at least once method is called twice" do - @double.should_receive(:do_something).at_least(:once) + expect(@double).to receive(:do_something).at_least(:once) @double.do_something @double.do_something verify @double end it "passes when at least twice method is called three times" do - @double.should_receive(:do_something).at_least(:twice) + expect(@double).to receive(:do_something).at_least(:twice) @double.do_something @double.do_something @double.do_something @@ -83,14 +83,14 @@ module Mocks end it "passes when at least twice method is called twice" do - @double.should_receive(:do_something).at_least(:twice) + expect(@double).to receive(:do_something).at_least(:twice) @double.do_something @double.do_something verify @double end it "returns the value given by a block when the at least once method is called" do - @double.should_receive(:to_s).at_least(:once) { "testing" } + expect(@double).to receive(:to_s).at_least(:once) { "testing" } expect(@double.to_s).to eq "testing" verify @double end @@ -104,15 +104,15 @@ module Mocks end it "uses a stub value if no value set" do - @double.stub(:do_something => 'foo') - @double.should_receive(:do_something).at_least(:once) + allow(@double).to receive_messages(:do_something => 'foo') + expect(@double).to receive(:do_something).at_least(:once) expect(@double.do_something).to eq 'foo' expect(@double.do_something).to eq 'foo' end it "prefers its own return value over a stub" do - @double.stub(:do_something => 'foo') - @double.should_receive(:do_something).at_least(:once).and_return('bar') + allow(@double).to receive_messages(:do_something => 'foo') + expect(@double).to receive(:do_something).at_least(:once).and_return('bar') expect(@double.do_something).to eq 'bar' expect(@double.do_something).to eq 'bar' end diff --git a/spec/rspec/mocks/at_most_spec.rb b/spec/rspec/mocks/at_most_spec.rb index a0a5a18a5..956619a67 100644 --- a/spec/rspec/mocks/at_most_spec.rb +++ b/spec/rspec/mocks/at_most_spec.rb @@ -6,60 +6,60 @@ module Mocks end it "passes when at_most(n) is called exactly n times" do - @double.should_receive(:do_something).at_most(2).times + expect(@double).to receive(:do_something).at_most(2).times @double.do_something @double.do_something verify @double end it "passes when at_most(n) is called less than n times" do - @double.should_receive(:do_something).at_most(2).times + expect(@double).to receive(:do_something).at_most(2).times @double.do_something verify @double end it "passes when at_most(n) is never called" do - @double.should_receive(:do_something).at_most(2).times + expect(@double).to receive(:do_something).at_most(2).times verify @double end it "passes when at_most(:once) is called once" do - @double.should_receive(:do_something).at_most(:once) + expect(@double).to receive(:do_something).at_most(:once) @double.do_something verify @double end it "passes when at_most(:once) is never called" do - @double.should_receive(:do_something).at_most(:once) + expect(@double).to receive(:do_something).at_most(:once) verify @double end it "passes when at_most(:twice) is called once" do - @double.should_receive(:do_something).at_most(:twice) + expect(@double).to receive(:do_something).at_most(:twice) @double.do_something verify @double end it "passes when at_most(:twice) is called twice" do - @double.should_receive(:do_something).at_most(:twice) + expect(@double).to receive(:do_something).at_most(:twice) @double.do_something @double.do_something verify @double end it "passes when at_most(:twice) is never called" do - @double.should_receive(:do_something).at_most(:twice) + expect(@double).to receive(:do_something).at_most(:twice) verify @double end it "returns the value given by a block when at_most(:once) method is called" do - @double.should_receive(:to_s).at_most(:once) { "testing" } + expect(@double).to receive(:to_s).at_most(:once) { "testing" } expect(@double.to_s).to eq "testing" verify @double end it "fails fast when at_most(n) times method is called n plus 1 times" do - @double.should_receive(:do_something).at_most(2).times + expect(@double).to receive(:do_something).at_most(2).times @double.do_something @double.do_something expect { @@ -68,7 +68,7 @@ module Mocks end it "fails fast when at_most(:once) and is called twice" do - @double.should_receive(:do_something).at_most(:once) + expect(@double).to receive(:do_something).at_most(:once) @double.do_something expect { @double.do_something @@ -76,7 +76,7 @@ module Mocks end it "fails fast when at_most(:twice) and is called three times" do - @double.should_receive(:do_something).at_most(:twice) + expect(@double).to receive(:do_something).at_most(:twice) @double.do_something @double.do_something expect { diff --git a/spec/rspec/mocks/before_all_spec.rb b/spec/rspec/mocks/before_all_spec.rb index 17f6675b8..2fa4eea58 100644 --- a/spec/rspec/mocks/before_all_spec.rb +++ b/spec/rspec/mocks/before_all_spec.rb @@ -51,7 +51,7 @@ def use_rspec_mocks describe "#stub" do include_examples "fails in a before(:all) block" do def use_rspec_mocks - Object.stub(:foo) + allow(Object).to receive(:foo) end end end @@ -67,7 +67,7 @@ def use_rspec_mocks describe "#should_receive" do include_examples "fails in a before(:all) block" do def use_rspec_mocks - Object.should_receive(:foo) + expect(Object).to receive(:foo) end end end @@ -75,7 +75,7 @@ def use_rspec_mocks describe "#should_not_receive" do include_examples "fails in a before(:all) block" do def use_rspec_mocks - Object.should_not_receive(:foo) + expect(Object).not_to receive(:foo) end end end @@ -83,7 +83,7 @@ def use_rspec_mocks describe "#any_instance" do include_examples "fails in a before(:all) block" do def use_rspec_mocks - Object.any_instance.should_receive(:foo) + expect_any_instance_of(Object).to receive(:foo) end end end @@ -91,7 +91,7 @@ def use_rspec_mocks describe "#stub_chain" do include_examples "fails in a before(:all) block" do def use_rspec_mocks - Object.stub_chain(:foo) + allow(Object).to receive_message_chain(:foo) end end end diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 48f0ce882..76f067ad6 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -2,15 +2,15 @@ describe "should_receive" do it "returns the value of executing the block" do obj = Object.new - obj.should_receive(:foo) { 'bar' } + expect(obj).to receive(:foo) { 'bar' } expect(obj.foo).to eq('bar') end it "works when a multi-return stub has already been set" do obj = Object.new return_value = Object.new - obj.stub(:foo).and_return(return_value, nil) - obj.should_receive(:foo) { return_value } + allow(obj).to receive(:foo).and_return(return_value, nil) + expect(obj).to receive(:foo) { return_value } expect(obj.foo).to be(return_value) end end @@ -18,7 +18,7 @@ describe "stub" do it "returns the value of executing the block" do obj = Object.new - obj.stub(:foo) { 'bar' } + allow(obj).to receive(:foo) { 'bar' } expect(obj.foo).to eq('bar') end @@ -37,13 +37,13 @@ describe "with" do it "returns the value of executing the block" do obj = Object.new - obj.stub(:foo).with('baz') { 'bar' } + allow(obj).to receive(:foo).with('baz') { 'bar' } expect(obj.foo('baz')).to eq('bar') end it "returns the value of executing the block with given argument" do obj = Object.new - obj.stub(:foo).with('baz') {|x| 'bar' + x } + allow(obj).to receive(:foo).with('baz') {|x| 'bar' + x } expect(obj.foo('baz')).to eq('barbaz') end end @@ -52,7 +52,7 @@ describe method do it "returns the value of executing the block" do obj = Object.new - obj.stub(:foo).send(method) { 'bar' } + allow(obj).to receive(:foo).send(method) { 'bar' } expect(obj.foo).to eq('bar') end end @@ -61,7 +61,7 @@ describe "times" do it "returns the value of executing the block" do obj = Object.new - obj.stub(:foo).at_least(1).times { 'bar' } + allow(obj).to receive(:foo).at_least(1).times { 'bar' } expect(obj.foo('baz')).to eq('bar') end end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index c5a1fdab5..54d475c02 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -3,7 +3,7 @@ module Mocks describe "Combining implementation instructions" do it 'can combine and_yield and and_return' do dbl = double - dbl.stub(:foo).and_yield(5).and_return(3) + allow(dbl).to receive(:foo).and_yield(5).and_return(3) expect { |b| expect(dbl.foo(&b)).to eq(3) @@ -24,43 +24,43 @@ def verify_combined_implementation it 'works when passing a block to `stub`' do verify_combined_implementation do |dbl| - dbl.stub(:foo) { @block_called = true } + allow(dbl).to receive(:foo) { @block_called = true } end end it 'works when passing a block to `with`' do verify_combined_implementation do |dbl| - dbl.stub(:foo).with(:arg) { @block_called = true } + allow(dbl).to receive(:foo).with(:arg) { @block_called = true } end end it 'works when passing a block to `exactly`' do verify_combined_implementation do |dbl| - dbl.should_receive(:foo).exactly(:once) { @block_called = true } + expect(dbl).to receive(:foo).exactly(:once) { @block_called = true } end end it 'works when passing a block to `at_least`' do verify_combined_implementation do |dbl| - dbl.should_receive(:foo).at_least(:once) { @block_called = true } + expect(dbl).to receive(:foo).at_least(:once) { @block_called = true } end end it 'works when passing a block to `at_most`' do verify_combined_implementation do |dbl| - dbl.should_receive(:foo).at_most(:once) { @block_called = true } + expect(dbl).to receive(:foo).at_most(:once) { @block_called = true } end end it 'works when passing a block to `times`' do verify_combined_implementation do |dbl| - dbl.should_receive(:foo).exactly(1).times { @block_called = true } + expect(dbl).to receive(:foo).exactly(1).times { @block_called = true } end end it 'works when passing a block to `once`' do verify_combined_implementation do |dbl| - dbl.should_receive(:foo).once { @block_called = true } + expect(dbl).to receive(:foo).once { @block_called = true } end end @@ -69,7 +69,7 @@ def verify_combined_implementation verify_combined_implementation do |dbl| the_double = dbl - dbl.should_receive(:foo).twice { @block_called = true } + expect(dbl).to receive(:foo).twice { @block_called = true } end the_double.foo { |a| } # to ensure it is called twice @@ -77,14 +77,14 @@ def verify_combined_implementation it 'works when passing a block to `ordered`' do verify_combined_implementation do |dbl| - dbl.should_receive(:foo).ordered { @block_called = true } + expect(dbl).to receive(:foo).ordered { @block_called = true } end end end it 'can combine and_yield and and_raise' do dbl = double - dbl.stub(:foo).and_yield(5).and_raise("boom") + allow(dbl).to receive(:foo).and_yield(5).and_raise("boom") expect { |b| expect { dbl.foo(&b) }.to raise_error("boom") @@ -94,7 +94,7 @@ def verify_combined_implementation it 'can combine and_yield, a block implementation and and_raise' do dbl = double block_called = false - dbl.stub(:foo) { block_called = true }.and_yield(5).and_raise("boom") + allow(dbl).to receive(:foo) { block_called = true }.and_yield(5).and_raise("boom") expect { |b| expect { dbl.foo(&b) }.to raise_error("boom") @@ -105,7 +105,7 @@ def verify_combined_implementation it 'can combine and_yield and and_throw' do dbl = double - dbl.stub(:foo).and_yield(5).and_throw(:bar) + allow(dbl).to receive(:foo).and_yield(5).and_throw(:bar) expect { |b| expect { dbl.foo(&b) }.to throw_symbol(:bar) @@ -115,7 +115,7 @@ def verify_combined_implementation it 'can combine and_yield, a block implementation and and_throw' do dbl = double block_called = false - dbl.stub(:foo) { block_called = true }.and_yield(5).and_throw(:bar) + allow(dbl).to receive(:foo) { block_called = true }.and_yield(5).and_throw(:bar) expect { |b| expect { dbl.foo(&b) }.to throw_symbol(:bar) @@ -126,7 +126,7 @@ def verify_combined_implementation it 'allows the terminal action to be overriden' do dbl = double - stubbed_double = dbl.stub(:foo) + stubbed_double = allow(dbl).to receive(:foo) stubbed_double.and_return(1) expect(dbl.foo).to eq(1) @@ -144,7 +144,7 @@ def verify_combined_implementation it 'allows the inner implementation block to be overriden' do allow(RSpec).to receive(:warning) dbl = double - stubbed_double = dbl.stub(:foo) + stubbed_double = allow(dbl).to receive(:foo) stubbed_double.with(:arg) { :with_block } expect(dbl.foo(:arg)).to eq(:with_block) @@ -155,21 +155,21 @@ def verify_combined_implementation it 'warns when the inner implementation block is overriden' do expect(RSpec).to receive(:warning).with(/overriding a previous stub implementation of `foo`.*#{__FILE__}:#{__LINE__ + 1}/) - double.stub(:foo).with(:arg) { :with_block }.at_least(:once) { :at_least_block } + allow(double).to receive(:foo).with(:arg) { :with_block }.at_least(:once) { :at_least_block } end it "does not warn about overriding the stub when `:with` is chained off the block" do expect(RSpec).not_to receive(:warning) obj = Object.new - stub = obj.stub(:foo) { } + stub = allow(obj).to receive(:foo) { } stub.with(1) end it 'can combine and_call_original, with, and_return' do obj = Struct.new(:value).new('original') - obj.stub(:value).and_call_original - obj.stub(:value).with(:arg).and_return('value') + allow(obj).to receive(:value).and_call_original + allow(obj).to receive(:value).with(:arg).and_return('value') expect(obj.value).to eq 'original' expect(obj.value(:arg)).to eq 'value' end @@ -177,7 +177,7 @@ def verify_combined_implementation it 'raises an error if `and_call_original` is followed by any other instructions' do allow(RSpec).to receive(:warning) dbl = [1, 2, 3] - stubbed = dbl.stub(:size) + stubbed = allow(dbl).to receive(:size) stubbed.and_call_original msg_fragment = /cannot be modified further/ diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 7a14911e8..4d31db320 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -115,8 +115,8 @@ def sandboxed end it "does not warn about the should syntax" do - RSpec.should_not_receive(:deprecate) - Object.new.should_not_receive(:bees) + expect(RSpec).not_to receive(:deprecate) + expect(Object.new).not_to receive(:bees) end end end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index ea4e98464..246319c3b 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -109,7 +109,7 @@ module Mocks end it "reports line number of expectation of unreceived message" do - expected_error_line = __LINE__; @double.should_receive(:wont_happen).with("x", 3) + expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) begin verify @double violated @@ -120,8 +120,8 @@ module Mocks end it "reports line number of expectation of unreceived message after #should_receive after similar stub" do - @double.stub(:wont_happen) - expected_error_line = __LINE__; @double.should_receive(:wont_happen).with("x", 3) + allow(@double).to receive(:wont_happen) + expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) begin verify @double violated @@ -132,19 +132,19 @@ module Mocks end it "passes when not receiving message specified as not to be received" do - @double.should_not_receive(:not_expected) + expect(@double).not_to receive(:not_expected) verify @double end it "prevents confusing double-negative expressions involving `never`" do expect { - @double.should_not_receive(:not_expected).never + expect(@double).not_to receive(:not_expected).never }.to raise_error(/trying to negate it again/) end it "warns when `and_return` is called on a negative expectation" do expect { - @double.should_not_receive(:do_something).and_return(1) + expect(@double).not_to receive(:do_something).and_return(1) }.to raise_error(/not supported/) expect { @@ -157,14 +157,14 @@ module Mocks end it "passes when receiving message specified as not to be received with different args" do - @double.should_not_receive(:message).with("unwanted text") - @double.should_receive(:message).with("other text") + expect(@double).not_to receive(:message).with("unwanted text") + expect(@double).to receive(:message).with("other text") @double.message "other text" verify @double end it "fails when receiving message specified as not to be received" do - @double.should_not_receive(:not_expected) + expect(@double).not_to receive(:not_expected) expect { @double.not_expected violated @@ -175,7 +175,7 @@ module Mocks end it "fails when receiving message specified as not to be received with args" do - @double.should_not_receive(:not_expected).with("unexpected text") + expect(@double).not_to receive(:not_expected).with("unexpected text") expect { @double.not_expected("unexpected text") violated @@ -186,7 +186,7 @@ module Mocks end it "fails when array arguments do not match" do - @double.should_not_receive(:not_expected).with(["do not want"]) + expect(@double).not_to receive(:not_expected).with(["do not want"]) expect { @double.not_expected(["do not want"]) violated @@ -199,7 +199,7 @@ module Mocks context "when specifying a message should not be received with specific args" do context "using `should_not_receive`" do it 'passes when receiving the message with different args' do - @double.should_not_receive(:not_expected).with("unexpected text") + expect(@double).not_to receive(:not_expected).with("unexpected text") @double.not_expected "really unexpected text" verify @double end @@ -207,7 +207,7 @@ module Mocks context "using `should_receive().never`" do it 'passes when receiving the message with different args' do - @double.should_receive(:not_expected).with("unexpected text").never + expect(@double).to receive(:not_expected).with("unexpected text").never @double.not_expected "really unexpected text" verify @double end @@ -215,17 +215,17 @@ module Mocks end it 'does not get confused when `should_not_received` is used with a string and symbol message' do - @double.stub(:foo) { 3 } - @double.should_not_receive(:foo).with(1) - @double.should_not_receive("foo").with(2) + allow(@double).to receive(:foo) { 3 } + expect(@double).not_to receive(:foo).with(1) + expect(@double).not_to receive("foo").with(2) expect(@double.foo).to eq(3) verify @double end it 'does not get confused when `should_received` is used with a string and symbol message' do - @double.should_receive(:foo).with(1) - @double.should_receive("foo").with(2) + expect(@double).to receive(:foo).with(1) + expect(@double).to receive("foo").with(2) @double.foo(1) @double.foo(2) @@ -234,26 +234,26 @@ module Mocks end it "allows parameter as return value" do - @double.should_receive(:something).with("a","b","c").and_return("booh") + expect(@double).to receive(:something).with("a","b","c").and_return("booh") expect(@double.something("a","b","c")).to eq "booh" verify @double end it "returns the previously stubbed value if no return value is set" do - @double.stub(:something).with("a","b","c").and_return(:stubbed_value) - @double.should_receive(:something).with("a","b","c") + allow(@double).to receive(:something).with("a","b","c").and_return(:stubbed_value) + expect(@double).to receive(:something).with("a","b","c") expect(@double.something("a","b","c")).to eq :stubbed_value verify @double end it "returns nil if no return value is set and there is no previously stubbed value" do - @double.should_receive(:something).with("a","b","c") + expect(@double).to receive(:something).with("a","b","c") expect(@double.something("a","b","c")).to be_nil verify @double end it "raises exception if args don't match when method called" do - @double.should_receive(:something).with("a","b","c").and_return("booh") + expect(@double).to receive(:something).with("a","b","c").and_return("booh") expect { @double.something("a","d","c") violated @@ -262,8 +262,8 @@ module Mocks describe "even when a similar expectation with different arguments exist" do it "raises exception if args don't match when method called, correctly reporting the offending arguments" do - @double.should_receive(:something).with("a","b","c").once - @double.should_receive(:something).with("z","x","c").once + expect(@double).to receive(:something).with("a","b","c").once + expect(@double).to receive(:something).with("z","x","c").once expect { @double.something("a","b","c") @double.something("z","x","g") @@ -272,8 +272,8 @@ module Mocks end it "raises exception if args don't match when method called even when the method is stubbed" do - @double.stub(:something) - @double.should_receive(:something).with("a","b","c") + allow(@double).to receive(:something) + expect(@double).to receive(:something).with("a","b","c") expect { @double.something("a","d","c") verify @double @@ -282,7 +282,7 @@ module Mocks it "raises exception if args don't match when method called even when using null_object" do @double = double("test double").as_null_object - @double.should_receive(:something).with("a","b","c") + expect(@double).to receive(:something).with("a","b","c") expect { @double.something("a","d","c") verify @double @@ -292,7 +292,7 @@ module Mocks describe 'with a method that has a default argument' do it "raises an exception if the arguments don't match when the method is called, correctly reporting the offending arguments" do def @double.method_with_default_argument(arg={}); end - @double.should_receive(:method_with_default_argument).with({}) + expect(@double).to receive(:method_with_default_argument).with({}) expect { @double.method_with_default_argument(nil) @@ -309,7 +309,7 @@ def @double.method_with_default_argument(arg={}); end end it "uses block for expectation if provided" do - @double.should_receive(:something) do | a, b | + expect(@double).to receive(:something) do | a, b | expect(a).to eq "a" expect(b).to eq "b" "booh" @@ -319,7 +319,7 @@ def @double.method_with_default_argument(arg={}); end end it "fails if expectation block fails" do - @double.should_receive(:something) do |bool| + expect(@double).to receive(:something) do |bool| expect(bool).to be_truthy end @@ -355,27 +355,27 @@ def @double.method_with_default_argument(arg={}); end end it "passes proc to expectation block without an argument" do - @double.should_receive(:foo) { |&block| expect(block.call).to eq(:bar) } + expect(@double).to receive(:foo) { |&block| expect(block.call).to eq(:bar) } @double.foo { :bar } end it "passes proc to expectation block with an argument" do - @double.should_receive(:foo) { |arg, &block| expect(block.call).to eq(:bar) } + expect(@double).to receive(:foo) { |arg, &block| expect(block.call).to eq(:bar) } @double.foo(:arg) { :bar } end it "passes proc to stub block without an argurment" do - @double.stub(:foo) { |&block| expect(block.call).to eq(:bar) } + allow(@double).to receive(:foo) { |&block| expect(block.call).to eq(:bar) } @double.foo { :bar } end it "passes proc to stub block with an argument" do - @double.stub(:foo) { |arg, &block| expect(block.call).to eq(:bar) } + allow(@double).to receive(:foo) { |arg, &block| expect(block.call).to eq(:bar) } @double.foo(:arg) { :bar } end it "fails right away when method defined as never is received" do - @double.should_receive(:not_expected).never + expect(@double).to receive(:not_expected).never expect { @double.not_expected }. to raise_error(RSpec::Mocks::MockExpectationError, %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| @@ -383,27 +383,27 @@ def @double.method_with_default_argument(arg={}); end end it "raises RuntimeError by default" do - @double.should_receive(:something).and_raise + expect(@double).to receive(:something).and_raise expect { @double.something }.to raise_error(RuntimeError) end it "raises RuntimeError with a message by default" do - @double.should_receive(:something).and_raise("error message") + expect(@double).to receive(:something).and_raise("error message") expect { @double.something }.to raise_error(RuntimeError, "error message") end it "raises an exception of a given type without an error message" do - @double.should_receive(:something).and_raise(StandardError) + expect(@double).to receive(:something).and_raise(StandardError) expect { @double.something }.to raise_error(StandardError) end it "raises an exception of a given type with a message" do - @double.should_receive(:something).and_raise(RuntimeError, "error message") + expect(@double).to receive(:something).and_raise(RuntimeError, "error message") expect { @double.something }.to raise_error(RuntimeError, "error message") end it "raises a given instance of an exception" do - @double.should_receive(:something).and_raise(RuntimeError.new("error message")) + expect(@double).to receive(:something).and_raise(RuntimeError.new("error message")) expect { @double.something }.to raise_error(RuntimeError, "error message") end @@ -416,7 +416,7 @@ def initialize(amount, units) end it "raises a given instance of an exception with arguments other than the standard 'message'" do - @double.should_receive(:something).and_raise(OutOfGas.new(2, :oz)) + expect(@double).to receive(:something).and_raise(OutOfGas.new(2, :oz)) begin @double.something @@ -428,21 +428,21 @@ def initialize(amount, units) end it "does not raise when told to if args dont match" do - @double.should_receive(:something).with(2).and_raise(RuntimeError) + expect(@double).to receive(:something).with(2).and_raise(RuntimeError) expect { @double.something 1 }.to raise_error(RSpec::Mocks::MockExpectationError) end it "throws when told to" do - @double.should_receive(:something).and_throw(:blech) + expect(@double).to receive(:something).and_throw(:blech) expect { @double.something }.to throw_symbol(:blech) end it "ignores args on any args" do - @double.should_receive(:something).at_least(:once).with(any_args) + expect(@double).to receive(:something).at_least(:once).with(any_args) @double.something @double.something 1 @double.something "a", 2 @@ -451,28 +451,28 @@ def initialize(amount, units) end it "fails on no args if any args received" do - @double.should_receive(:something).with(no_args()) + expect(@double).to receive(:something).with(no_args()) expect { @double.something 1 }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (no args)\n got: (1)") end it "fails when args are expected but none are received" do - @double.should_receive(:something).with(1) + expect(@double).to receive(:something).with(1) expect { @double.something }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (1)\n got: (no args)") end it "returns value from block by default" do - @double.stub(:method_that_yields).and_yield + allow(@double).to receive(:method_that_yields).and_yield value = @double.method_that_yields { :returned_obj } expect(value).to eq :returned_obj verify @double end it "yields 0 args to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield + expect(@double).to receive(:yield_back).with(no_args()).once.and_yield a = nil @double.yield_back {|*x| a = x} expect(a).to eq [] @@ -480,7 +480,7 @@ def initialize(amount, units) end it "yields 0 args multiple times to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield. + expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield. and_yield b = [] @double.yield_back {|*a| b << a} @@ -489,7 +489,7 @@ def initialize(amount, units) end it "yields one arg to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield(99) + expect(@double).to receive(:yield_back).with(no_args()).once.and_yield(99) a = nil @double.yield_back {|*x| a = x} expect(a).to eq [99] @@ -497,7 +497,7 @@ def initialize(amount, units) end it "yields one arg 3 times consecutively to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99). + expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield(99). and_yield(43). and_yield("something fruity") b = [] @@ -507,7 +507,7 @@ def initialize(amount, units) end it "yields many args to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield(99, 27, "go") + expect(@double).to receive(:yield_back).with(no_args()).once.and_yield(99, 27, "go") a = nil @double.yield_back {|*x| a = x} expect(a).to eq [99, 27, "go"] @@ -515,7 +515,7 @@ def initialize(amount, units) end it "yields many args 3 times consecutively to blocks that take a variable number of arguments" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99, :green, "go"). + expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield(99, :green, "go"). and_yield("wait", :amber). and_yield("stop", 12, :red) b = [] @@ -525,7 +525,7 @@ def initialize(amount, units) end it "yields single value" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield(99) + expect(@double).to receive(:yield_back).with(no_args()).once.and_yield(99) a = nil @double.yield_back {|x| a = x} expect(a).to eq 99 @@ -533,7 +533,7 @@ def initialize(amount, units) end it "yields single value 3 times consecutively" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield(99). + expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield(99). and_yield(43). and_yield("something fruity") b = [] @@ -543,7 +543,7 @@ def initialize(amount, units) end it "yields two values" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') a, b = nil @double.yield_back {|x,y| a=x; b=y} expect(a).to eq 'wha' @@ -552,7 +552,7 @@ def initialize(amount, units) end it "yields two values 3 times consecutively" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). + expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). and_yield('not', 'down'). and_yield(14, 65) c = [] @@ -562,7 +562,7 @@ def initialize(amount, units) end it "fails when calling yielding method with wrong arity" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') expect { @double.yield_back {|a|} }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" yielded |\"wha\", \"zup\"| to block with arity of 1") @@ -570,7 +570,7 @@ def initialize(amount, units) if kw_args_supported? it 'fails when calling yielding method with invalid kw args' do - @double.should_receive(:yield_back).and_yield(:x => 1, :y => 2) + expect(@double).to receive(:yield_back).and_yield(:x => 1, :y => 2) expect { eval("@double.yield_back { |x: 1| }") }.to raise_error(RSpec::Mocks::MockExpectationError, @@ -579,7 +579,7 @@ def initialize(amount, units) end it "fails when calling yielding method consecutively with wrong arity" do - @double.should_receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). + expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). and_yield('down'). and_yield(14, 65) expect { @@ -589,20 +589,20 @@ def initialize(amount, units) end it "fails when calling yielding method without block" do - @double.should_receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') expect { @double.yield_back }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" asked to yield |[\"wha\", \"zup\"]| but no block was passed") end it "is able to double send" do - @double.should_receive(:send).with(any_args) + expect(@double).to receive(:send).with(any_args) @double.send 'hi' verify @double end it "is able to raise from method calling yielding double" do - @double.should_receive(:yield_me).and_yield 44 + expect(@double).to receive(:yield_me).and_yield 44 expect { @double.yield_me do |x| @@ -614,7 +614,7 @@ def initialize(amount, units) end it "clears expectations after verify" do - @double.should_receive(:foobar) + expect(@double).to receive(:foobar) @double.foobar verify @double expect { @@ -624,14 +624,14 @@ def initialize(amount, units) it "restores objects to their original state on rspec_reset" do double = double("this is a double") - double.should_receive(:blah) + expect(double).to receive(:blah) reset double verify double #should throw if reset didn't work end it "temporarily replaces a method stub on a double" do - @double.stub(:msg).and_return(:stub_value) - @double.should_receive(:msg).with(:arg).and_return(:double_value) + allow(@double).to receive(:msg).and_return(:stub_value) + expect(@double).to receive(:msg).with(:arg).and_return(:double_value) expect(@double.msg(:arg)).to equal(:double_value) expect(@double.msg).to equal(:stub_value) expect(@double.msg).to equal(:stub_value) @@ -639,8 +639,8 @@ def initialize(amount, units) end it "does not require a different signature to replace a method stub" do - @double.stub(:msg).and_return(:stub_value) - @double.should_receive(:msg).and_return(:double_value) + allow(@double).to receive(:msg).and_return(:stub_value) + expect(@double).to receive(:msg).and_return(:double_value) expect(@double.msg(:arg)).to equal(:double_value) expect(@double.msg).to equal(:stub_value) expect(@double.msg).to equal(:stub_value) @@ -648,15 +648,15 @@ def initialize(amount, units) end it "raises an error when a previously stubbed method has a negative expectation" do - @double.stub(:msg).and_return(:stub_value) - @double.should_not_receive(:msg) + allow(@double).to receive(:msg).and_return(:stub_value) + expect(@double).not_to receive(:msg) expect { @double.msg(:arg) }.to raise_error(RSpec::Mocks::MockExpectationError) end it "temporarily replaces a method stub on a non-double" do non_double = Object.new - non_double.stub(:msg).and_return(:stub_value) - non_double.should_receive(:msg).with(:arg).and_return(:double_value) + allow(non_double).to receive(:msg).and_return(:stub_value) + expect(non_double).to receive(:msg).with(:arg).and_return(:double_value) expect(non_double.msg(:arg)).to equal(:double_value) expect(non_double.msg).to equal(:stub_value) expect(non_double.msg).to equal(:stub_value) @@ -664,30 +664,30 @@ def initialize(amount, units) end it "returns the stubbed value when no new value specified" do - @double.stub(:msg).and_return(:stub_value) - @double.should_receive(:msg) + allow(@double).to receive(:msg).and_return(:stub_value) + expect(@double).to receive(:msg) expect(@double.msg).to equal(:stub_value) verify @double end it "returns the stubbed value when stubbed with args and no new value specified" do - @double.stub(:msg).with(:arg).and_return(:stub_value) - @double.should_receive(:msg).with(:arg) + allow(@double).to receive(:msg).with(:arg).and_return(:stub_value) + expect(@double).to receive(:msg).with(:arg) expect(@double.msg(:arg)).to equal(:stub_value) verify @double end it "does not mess with the stub's yielded values when also doubleed" do - @double.stub(:yield_back).and_yield(:stub_value) - @double.should_receive(:yield_back).and_yield(:double_value) + allow(@double).to receive(:yield_back).and_yield(:stub_value) + expect(@double).to receive(:yield_back).and_yield(:double_value) @double.yield_back{|v| expect(v).to eq :double_value } @double.yield_back{|v| expect(v).to eq :stub_value } verify @double end it "can yield multiple times when told to do so" do - @double.stub(:foo).and_yield(:stub_value) - @double.should_receive(:foo).and_yield(:first_yield).and_yield(:second_yield) + allow(@double).to receive(:foo).and_yield(:stub_value) + expect(@double).to receive(:foo).and_yield(:first_yield).and_yield(:second_yield) expect { |b| @double.foo(&b) }.to yield_successive_args(:first_yield, :second_yield) expect { |b| @double.foo(&b) }.to yield_with_args(:stub_value) @@ -711,7 +711,7 @@ def add_call end it "calls the block after #should_receive" do - @double.should_receive(:foo) { add_call } + expect(@double).to receive(:foo) { add_call } @double.foo @@ -719,8 +719,8 @@ def add_call end it "calls the block after #should_receive after a similar stub" do - @double.stub(:foo).and_return(:bar) - @double.should_receive(:foo) { add_call } + allow(@double).to receive(:foo).and_return(:bar) + expect(@double).to receive(:foo) { add_call } @double.foo @@ -728,7 +728,7 @@ def add_call end it "calls the block after #once" do - @double.should_receive(:foo).once { add_call } + expect(@double).to receive(:foo).once { add_call } @double.foo @@ -736,7 +736,7 @@ def add_call end it "calls the block after #twice" do - @double.should_receive(:foo).twice { add_call } + expect(@double).to receive(:foo).twice { add_call } @double.foo @double.foo @@ -745,7 +745,7 @@ def add_call end it "calls the block after #times" do - @double.should_receive(:foo).exactly(10).times { add_call } + expect(@double).to receive(:foo).exactly(10).times { add_call } (1..10).each { @double.foo } @@ -753,8 +753,8 @@ def add_call end it "calls the block after #ordered" do - @double.should_receive(:foo).ordered { add_call } - @double.should_receive(:bar).ordered { add_call } + expect(@double).to receive(:foo).ordered { add_call } + expect(@double).to receive(:bar).ordered { add_call } @double.foo @double.bar @@ -798,7 +798,7 @@ def add_call first = double('first') second = double('second') - first.should_receive(:==).with(second) + expect(first).to receive(:==).with(second) second == first end end @@ -808,14 +808,14 @@ def add_call context "with args" do context "with matching args" do it "passes" do - @double.should_receive(:foo).with('bar') + expect(@double).to receive(:foo).with('bar') @double.foo('bar') end end context "with non-matching args" do it "fails" do - @double.should_receive(:foo).with('bar') + expect(@double).to receive(:foo).with('bar') expect do @double.foo('baz') end.to raise_error @@ -827,7 +827,7 @@ def add_call it "fails" do d1 = double('1') d2 = double('2') - @double.should_receive(:foo).with(d1) + expect(@double).to receive(:foo).with(d1) expect do @double.foo(d2) end.to raise_error @@ -839,7 +839,7 @@ def add_call it "fails" do d1 = double('1').as_null_object d2 = double('2').as_null_object - @double.should_receive(:foo).with(d1) + expect(@double).to receive(:foo).with(d1) expect do @double.foo(d2) end.to raise_error @@ -851,14 +851,14 @@ def add_call context "with a block" do context "with matching args" do it "returns the result of the block" do - @double.should_receive(:foo).with('bar') { 'baz' } + expect(@double).to receive(:foo).with('bar') { 'baz' } expect(@double.foo('bar')).to eq('baz') end end context "with non-matching args" do it "fails" do - @double.should_receive(:foo).with('bar') { 'baz' } + expect(@double).to receive(:foo).with('bar') { 'baz' } expect do expect(@double.foo('wrong')).to eq('baz') end.to raise_error(/received :foo with unexpected arguments/) diff --git a/spec/rspec/mocks/failing_argument_matchers_spec.rb b/spec/rspec/mocks/failing_argument_matchers_spec.rb index 0ddcbf570..27058637a 100644 --- a/spec/rspec/mocks/failing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/failing_argument_matchers_spec.rb @@ -11,50 +11,50 @@ module Mocks end it "rejects non boolean" do - @double.should_receive(:random_call).with(boolean()) + expect(@double).to receive(:random_call).with(boolean()) expect do @double.random_call("false") end.to raise_error(RSpec::Mocks::MockExpectationError) end it "rejects non numeric" do - @double.should_receive(:random_call).with(an_instance_of(Numeric)) + expect(@double).to receive(:random_call).with(an_instance_of(Numeric)) expect do @double.random_call("1") end.to raise_error(RSpec::Mocks::MockExpectationError) end it "rejects non string" do - @double.should_receive(:random_call).with(an_instance_of(String)) + expect(@double).to receive(:random_call).with(an_instance_of(String)) expect do @double.random_call(123) end.to raise_error(RSpec::Mocks::MockExpectationError) end it "rejects goose when expecting a duck" do - @double.should_receive(:random_call).with(duck_type(:abs, :div)) + expect(@double).to receive(:random_call).with(duck_type(:abs, :div)) expect { @double.random_call("I don't respond to :abs or :div") }.to raise_error(RSpec::Mocks::MockExpectationError) end it "fails if regexp does not match submitted string" do - @double.should_receive(:random_call).with(/bcd/) + expect(@double).to receive(:random_call).with(/bcd/) expect { @double.random_call("abc") }.to raise_error(RSpec::Mocks::MockExpectationError) end it "fails if regexp does not match submitted regexp" do - @double.should_receive(:random_call).with(/bcd/) + expect(@double).to receive(:random_call).with(/bcd/) expect { @double.random_call(/bcde/) }.to raise_error(RSpec::Mocks::MockExpectationError) end it "fails for a hash w/ wrong values" do - @double.should_receive(:random_call).with(:a => "b", :c => "d") + expect(@double).to receive(:random_call).with(:a => "b", :c => "d") expect do @double.random_call(:a => "b", :c => "e") end.to raise_error(RSpec::Mocks::MockExpectationError, /Double "double" received :random_call with unexpected arguments\n expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)\n got: \(\{(:a=>\"b\", :c=>\"e\"|:c=>\"e\", :a=>\"b\")\}\)/) end it "fails for a hash w/ wrong keys" do - @double.should_receive(:random_call).with(:a => "b", :c => "d") + expect(@double).to receive(:random_call).with(:a => "b", :c => "d") expect do @double.random_call("a" => "b", "c" => "d") end.to raise_error(RSpec::Mocks::MockExpectationError, /Double "double" received :random_call with unexpected arguments\n expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)\n got: \(\{(\"a\"=>\"b\", \"c\"=>\"d\"|\"c\"=>\"d\", \"a\"=>\"b\")\}\)/) @@ -64,50 +64,50 @@ module Mocks # This spec is generating warnings on 1.8.7, not sure why so # this does with_isolated_stderr to kill them. @samphippen 3rd Jan 2013. expect do - @double.should_receive(:msg).with(equal(3)) + expect(@double).to receive(:msg).with(equal(3)) with_isolated_stderr { @double.msg(37) } end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (equal 3)\n got: (37)") end it "fails no_args with one arg" do expect do - @double.should_receive(:msg).with(no_args) + expect(@double).to receive(:msg).with(no_args) @double.msg(37) end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (no args)\n got: (37)") end it "fails hash_including with missing key" do expect do - @double.should_receive(:msg).with(hash_including(:a => 1)) + expect(@double).to receive(:msg).with(hash_including(:a => 1)) @double.msg({}) end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (hash_including(:a=>1))\n got: ({})") end it "fails array_including when args aren't array" do expect do - @double.should_receive(:msg).with(array_including(1,2,3)) + expect(@double).to receive(:msg).with(array_including(1,2,3)) @double.msg(1,2,3) end.to raise_error(/array_including\(1,2,3\)/) end it "fails array_including when arg doesn't contain all elements" do expect do - @double.should_receive(:msg).with(array_including(1,2,3)) + expect(@double).to receive(:msg).with(array_including(1,2,3)) @double.msg(1,2) end.to raise_error(/array_including\(1,2,3\)/) end it "fails with zero arguments" do expect do - @double.should_receive(:msg).with {|arg| expect(arg).to eq :received } + expect(@double).to receive(:msg).with {|arg| expect(arg).to eq :received } end.to raise_error(ArgumentError, /must have at least one argument/) end it "fails when given an arbitrary object that returns false from #===" do matcher = double - matcher.should_receive(:===).with(4).at_least(:once).and_return(false) + expect(matcher).to receive(:===).with(4).at_least(:once).and_return(false) - @double.should_receive(:foo).with(matcher) + expect(@double).to receive(:foo).with(matcher) expect { @double.foo(4) @@ -125,7 +125,7 @@ def inspect end.new expect do - @double.should_receive(:msg).with(3) + expect(@double).to receive(:msg).with(3) @double.msg arg end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (3)\n got: (my_thing)") end @@ -141,7 +141,7 @@ def inspect end.new expect do - @double.should_receive(:msg).with(arg) + expect(@double).to receive(:msg).with(arg) @double.msg 3 end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (my_thing)\n got: (3)") end @@ -158,20 +158,20 @@ def inspect end.new expect do - @double.should_receive(:msg).with(arg) + expect(@double).to receive(:msg).with(arg) @double.msg 3 end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (my_thing)\n got: (3)") end it "fails a class against an unrelated class" do - @double.should_receive(:foo).with(Fixnum) + expect(@double).to receive(:foo).with(Fixnum) expect { @double.foo(Hash) }.to raise_error(RSpec::Mocks::MockExpectationError) end it "fails a class against an object of a different type" do - @double.should_receive(:foo).with(Fixnum) + expect(@double).to receive(:foo).with(Fixnum) expect { @double.foo(3.2) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 8554b06c5..a1c1560f2 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -38,7 +38,7 @@ module Mocks it 'fails when the method has been mocked' do dbl = double - dbl.should_receive(:expected_method) + expect(dbl).to receive(:expected_method) dbl.expected_method expect { @@ -100,10 +100,10 @@ module Mocks it 'resets expectations on class methods when mocks are reset' do dbl = Object - dbl.stub(:expected_method) + allow(dbl).to receive(:expected_method) dbl.expected_method reset dbl - dbl.stub(:expected_method) + allow(dbl).to receive(:expected_method) expect { expect(dbl).to have_received(:expected_method) diff --git a/spec/rspec/mocks/mock_ordering_spec.rb b/spec/rspec/mocks/mock_ordering_spec.rb index 722bce30f..ea7327f79 100644 --- a/spec/rspec/mocks/mock_ordering_spec.rb +++ b/spec/rspec/mocks/mock_ordering_spec.rb @@ -6,19 +6,19 @@ module Mocks after { reset @double } it "passes when messages are received in order" do - @double.should_receive(:one).ordered - @double.should_receive(:two).ordered - @double.should_receive(:three).ordered + expect(@double).to receive(:one).ordered + expect(@double).to receive(:two).ordered + expect(@double).to receive(:three).ordered @double.one @double.two @double.three end it "passes when messages are received in order" do - @double.stub(:something) - @double.should_receive(:one).ordered - @double.should_receive(:two).ordered - @double.should_receive(:three).at_least(:once).ordered + allow(@double).to receive(:something) + expect(@double).to receive(:one).ordered + expect(@double).to receive(:two).ordered + expect(@double).to receive(:three).at_least(:once).ordered @double.one @double.two @double.three @@ -28,26 +28,26 @@ module Mocks it "passes when messages are received in order across objects" do a = double("a") b = double("b") - a.should_receive(:one).ordered - b.should_receive(:two).ordered - a.should_receive(:three).ordered + expect(a).to receive(:one).ordered + expect(b).to receive(:two).ordered + expect(a).to receive(:three).ordered a.one b.two a.three end it "fails when messages are received out of order (2nd message 1st)" do - @double.should_receive(:one).ordered - @double.should_receive(:two).ordered + expect(@double).to receive(:one).ordered + expect(@double).to receive(:two).ordered expect { @double.two }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :two out of order") end it "fails when messages are received out of order (3rd message 1st)" do - @double.should_receive(:one).ordered - @double.should_receive(:two).ordered - @double.should_receive(:three).ordered + expect(@double).to receive(:one).ordered + expect(@double).to receive(:two).ordered + expect(@double).to receive(:three).ordered @double.one expect { @double.three @@ -55,9 +55,9 @@ module Mocks end it "fails when messages are received out of order (3rd message 2nd)" do - @double.should_receive(:one).ordered - @double.should_receive(:two).ordered - @double.should_receive(:three).ordered + expect(@double).to receive(:one).ordered + expect(@double).to receive(:two).ordered + expect(@double).to receive(:three).ordered @double.one expect { @double.three @@ -67,9 +67,9 @@ module Mocks it "fails when messages are out of order across objects" do a = double("test double") b = double("another test double") - a.should_receive(:one).ordered - b.should_receive(:two).ordered - a.should_receive(:three).ordered + expect(a).to receive(:one).ordered + expect(b).to receive(:two).ordered + expect(a).to receive(:three).ordered a.one expect { a.three @@ -79,14 +79,14 @@ module Mocks end it "ignores order of non ordered messages" do - @double.should_receive(:ignored_0) - @double.should_receive(:ordered_1).ordered - @double.should_receive(:ignored_1) - @double.should_receive(:ordered_2).ordered - @double.should_receive(:ignored_2) - @double.should_receive(:ignored_3) - @double.should_receive(:ordered_3).ordered - @double.should_receive(:ignored_4) + expect(@double).to receive(:ignored_0) + expect(@double).to receive(:ordered_1).ordered + expect(@double).to receive(:ignored_1) + expect(@double).to receive(:ordered_2).ordered + expect(@double).to receive(:ignored_2) + expect(@double).to receive(:ignored_3) + expect(@double).to receive(:ordered_3).ordered + expect(@double).to receive(:ignored_4) @double.ignored_3 @double.ordered_1 @double.ignored_0 @@ -99,9 +99,9 @@ module Mocks end it "supports duplicate messages" do - @double.should_receive(:a).ordered - @double.should_receive(:b).ordered - @double.should_receive(:a).ordered + expect(@double).to receive(:a).ordered + expect(@double).to receive(:b).ordered + expect(@double).to receive(:a).ordered @double.a @double.b diff --git a/spec/rspec/mocks/multiple_return_value_spec.rb b/spec/rspec/mocks/multiple_return_value_spec.rb index b57ffac85..4c539917e 100644 --- a/spec/rspec/mocks/multiple_return_value_spec.rb +++ b/spec/rspec/mocks/multiple_return_value_spec.rb @@ -4,11 +4,11 @@ module Mocks let(:a_double) { double } before do - a_double.stub(:foo).and_return(:val_1, nil) + allow(a_double).to receive(:foo).and_return(:val_1, nil) end it 'can still set a message expectation with a single return value' do - a_double.should_receive(:foo).once.and_return(:val_1) + expect(a_double).to receive(:foo).once.and_return(:val_1) expect(a_double.foo).to eq(:val_1) end end @@ -17,7 +17,7 @@ module Mocks before(:each) do @double = double @return_values = [1,2,3] - @double.should_receive(:do_something).and_return(@return_values[0],@return_values[1],@return_values[2]) + expect(@double).to receive(:do_something).and_return(@return_values[0],@return_values[1],@return_values[2]) end it "returns values in order" do @@ -28,7 +28,7 @@ module Mocks end it "falls back to a previously stubbed value" do - @double.stub :do_something => :stub_result + allow(@double).to receive_messages :do_something => :stub_result expect(@double.do_something).to eq @return_values[0] expect(@double.do_something).to eq @return_values[1] expect(@double.do_something).to eq @return_values[2] @@ -54,7 +54,7 @@ module Mocks before(:each) do @double = double @return_values = [1,2,3] - @double.should_receive(:do_something).exactly(3).times.and_return(@return_values[0], @return_values[1], @return_values[2]) + expect(@double).to receive(:do_something).exactly(3).times.and_return(@return_values[0], @return_values[1], @return_values[2]) end it "returns values in order to consecutive calls" do @@ -68,7 +68,7 @@ module Mocks describe "a message expectation with multiple return values specifying at_least less than the number of values" do before(:each) do @double = double - @double.should_receive(:do_something).at_least(:twice).with(no_args).and_return(11, 22) + expect(@double).to receive(:do_something).at_least(:twice).with(no_args).and_return(11, 22) end it "uses the last return value for subsequent calls" do @@ -84,7 +84,7 @@ module Mocks end context "when method is stubbed too" do - before { @double.stub(:do_something).and_return :stub_result } + before { allow(@double).to receive(:do_something).and_return :stub_result } it "uses the last value for subsequent calls" do expect(@double.do_something).to equal(11) @@ -103,7 +103,7 @@ module Mocks describe "a message expectation with multiple return values with a specified count larger than the number of values" do before(:each) do @double = RSpec::Mocks::Double.new("double") - @double.should_receive(:do_something).exactly(3).times.and_return(11, 22) + expect(@double).to receive(:do_something).exactly(3).times.and_return(11, 22) end it "uses the last return value for subsequent calls" do diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index 4de403f9d..27c2aa26e 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -3,30 +3,30 @@ module Mocks describe "an expectation set on nil" do it "issues a warning with file and line number information" do expected_warning = %r%An expectation of :foo was set on nil. Called from #{__FILE__}:#{__LINE__+3}(:in .+)?. Use allow_message_expectations_on_nil to disable warnings.% - Kernel.should_receive(:warn).with(expected_warning) + expect(Kernel).to receive(:warn).with(expected_warning) - nil.should_receive(:foo) + expect(nil).to receive(:foo) nil.foo end it "issues a warning when the expectation is negative" do - Kernel.should_receive(:warn) + expect(Kernel).to receive(:warn) - nil.should_not_receive(:foo) + expect(nil).not_to receive(:foo) end it "does not issue a warning when expectations are set to be allowed" do allow_message_expectations_on_nil - Kernel.should_not_receive(:warn) + expect(Kernel).not_to receive(:warn) - nil.should_receive(:foo) - nil.should_not_receive(:bar) + expect(nil).to receive(:foo) + expect(nil).not_to receive(:bar) nil.foo end it 'does not call #nil? on a double extra times' do dbl = double - dbl.should_receive(:nil?).once.and_return(false) + expect(dbl).to receive(:nil?).once.and_return(false) dbl.nil? end end @@ -36,8 +36,8 @@ module Mocks allow_message_expectations_on_nil RSpec::Mocks.teardown RSpec::Mocks.setup - Kernel.should_receive(:warn) - nil.should_receive(:foo) + expect(Kernel).to receive(:warn) + expect(nil).to receive(:foo) nil.foo end diff --git a/spec/rspec/mocks/null_object_double_spec.rb b/spec/rspec/mocks/null_object_double_spec.rb index 5c134df10..495fcad29 100644 --- a/spec/rspec/mocks/null_object_double_spec.rb +++ b/spec/rspec/mocks/null_object_double_spec.rb @@ -10,7 +10,7 @@ module Mocks end it "says it responds to messages it does understand" do - @double.stub(:foo) + allow(@double).to receive(:foo) expect(@double).to respond_to(:foo) end @@ -35,12 +35,12 @@ module Mocks end it "allows explicit stubs" do - @double.stub(:foo) { "bar" } + allow(@double).to receive(:foo) { "bar" } expect(@double.foo).to eq("bar") end it "allows explicit expectation" do - @double.should_receive(:something) + expect(@double).to receive(:something) @double.something end @@ -49,19 +49,19 @@ module Mocks end it 'continues to return self from an explicit expectation' do - @double.should_receive(:bar) + expect(@double).to receive(:bar) expect(@double.foo.bar).to be(@double) end it 'returns an explicitly stubbed value from an expectation with no implementation' do - @double.stub(:foo => "bar") - @double.should_receive(:foo) + allow(@double).to receive_messages(:foo => "bar") + expect(@double).to receive(:foo) expect(@double.foo).to eq("bar") end it "fails verification when explicit exception not met" do expect { - @double.should_receive(:something) + expect(@double).to receive(:something) verify @double }.to raise_error(RSpec::Mocks::MockExpectationError) end @@ -82,13 +82,13 @@ module Mocks end it "allows expected message with different args first" do - @double.should_receive(:message).with(:expected_arg) + expect(@double).to receive(:message).with(:expected_arg) @double.message(:unexpected_arg) @double.message(:expected_arg) end it "allows expected message with different args second" do - @double.should_receive(:message).with(:expected_arg) + expect(@double).to receive(:message).with(:expected_arg) @double.message(:expected_arg) @double.message(:unexpected_arg) end diff --git a/spec/rspec/mocks/once_counts_spec.rb b/spec/rspec/mocks/once_counts_spec.rb index e5b643e92..825cc3c0b 100644 --- a/spec/rspec/mocks/once_counts_spec.rb +++ b/spec/rspec/mocks/once_counts_spec.rb @@ -6,25 +6,25 @@ module Mocks end it "passes when called once" do - @double.should_receive(:do_something).once + expect(@double).to receive(:do_something).once @double.do_something verify @double end it "passes when called once with specified args" do - @double.should_receive(:do_something).once.with("a", "b", "c") + expect(@double).to receive(:do_something).once.with("a", "b", "c") @double.do_something("a", "b", "c") verify @double end it "passes when called once with unspecified args" do - @double.should_receive(:do_something).once + expect(@double).to receive(:do_something).once @double.do_something("a", "b", "c") verify @double end it "fails when called with wrong args" do - @double.should_receive(:do_something).once.with("a", "b", "c") + expect(@double).to receive(:do_something).once.with("a", "b", "c") expect { @double.do_something("d", "e", "f") }.to raise_error(RSpec::Mocks::MockExpectationError) @@ -32,7 +32,7 @@ module Mocks end it "fails fast when called twice" do - @double.should_receive(:do_something).once + expect(@double).to receive(:do_something).once @double.do_something expect { @double.do_something @@ -40,7 +40,7 @@ module Mocks end it "fails when not called" do - @double.should_receive(:do_something).once + expect(@double).to receive(:do_something).once expect { verify @double }.to raise_error(RSpec::Mocks::MockExpectationError) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 9d52b6c2a..e378076a3 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -4,14 +4,14 @@ module Mocks let(:object) { Object.new } it "names the class in the failure message" do - object.should_receive(:foo) + expect(object).to receive(:foo) expect do verify object end.to raise_error(RSpec::Mocks::MockExpectationError, /\(#\).foo/) end it "names the class in the failure message when expectation is on class" do - Object.should_receive(:foo) + expect(Object).to receive(:foo) expect { verify Object }.to raise_error(RSpec::Mocks::MockExpectationError, //) @@ -19,12 +19,12 @@ module Mocks it "does not conflict with @options in the object" do object.instance_exec { @options = Object.new } - object.should_receive(:blah) + expect(object).to receive(:blah) object.blah end it "should_not_receive mocks out the method" do - object.should_not_receive(:fuhbar) + expect(object).not_to receive(:fuhbar) expect { object.fuhbar }.to raise_error( @@ -34,40 +34,40 @@ module Mocks end it "should_receive mocks out the method" do - object.should_receive(:foobar).with(:test_param).and_return(1) + expect(object).to receive(:foobar).with(:test_param).and_return(1) expect(object.foobar(:test_param)).to equal(1) end it "should_receive handles a hash" do - object.should_receive(:foobar).with(:key => "value").and_return(1) + expect(object).to receive(:foobar).with(:key => "value").and_return(1) expect(object.foobar(:key => "value")).to equal(1) end it "should_receive handles an inner hash" do hash = {:a => {:key => "value"}} - object.should_receive(:foobar).with(:key => "value").and_return(1) + expect(object).to receive(:foobar).with(:key => "value").and_return(1) expect(object.foobar(hash[:a])).to equal(1) end it "should_receive returns a positive message expectation" do - expect(object.should_receive(:foobar)).not_to be_negative + expect(expect(object).to receive(:foobar)).not_to be_negative object.foobar end it "should_receive verifies method was called" do - object.should_receive(:foobar).with(:test_param).and_return(1) + expect(object).to receive(:foobar).with(:test_param).and_return(1) expect { verify object }.to raise_error(RSpec::Mocks::MockExpectationError) end it "should_receive also takes a String argument" do - object.should_receive('foobar') + expect(object).to receive('foobar') object.foobar end it "should_not_receive also takes a String argument" do - object.should_not_receive('foobar') + expect(object).not_to receive('foobar') expect { object.foobar }.to raise_error(RSpec::Mocks::MockExpectationError) @@ -77,7 +77,7 @@ module Mocks allow_message_expectations_on_nil _nil = nil - _nil.should_receive(:foobar) + expect(_nil).to receive(:foobar) expect { verify _nil }.to raise_error( @@ -93,7 +93,7 @@ def self.inspect end end - klass.should_receive(:bar).with(1) + expect(klass).to receive(:bar).with(1) klass.bar(1) expect { @@ -123,13 +123,13 @@ def method_missing(*a) let(:proxy) { proxy_class.new(wrapped_object) } it 'works properly' do - proxy.should_receive(:proxied?).and_return(false) + expect(proxy).to receive(:proxied?).and_return(false) expect(proxy).not_to be_proxied end it 'does not confuse the proxy and the proxied object' do - proxy.stub(:foo).and_return(:proxy_foo) - wrapped_object.stub(:foo).and_return(:wrapped_foo) + allow(proxy).to receive(:foo).and_return(:proxy_foo) + allow(wrapped_object).to receive(:foo).and_return(:wrapped_foo) expect(proxy.foo).to eq(:proxy_foo) expect(wrapped_object.foo).to eq(:wrapped_foo) @@ -156,7 +156,7 @@ def ==(other) it "does not raise an error when stubbing the object" do o = klass.new :foo - expect { o.stub(:bar) }.not_to raise_error + expect { allow(o).to receive(:bar) }.not_to raise_error end end @@ -176,7 +176,7 @@ def ==(other) describe "stubbing a base class class method" do before do - klass.stub(:find).and_return "stubbed_value" + allow(klass).to receive(:find).and_return "stubbed_value" end it "returns the value for the stub on the base class" do @@ -206,19 +206,19 @@ def private_method; end let(:object) { klass.new } it 'keeps public methods public' do - object.should_receive(:public_method) + expect(object).to receive(:public_method) expect(object.public_methods).to include_method(:public_method) object.public_method end it 'keeps private methods private' do - object.should_receive(:private_method) + expect(object).to receive(:private_method) expect(object.private_methods).to include_method(:private_method) object.public_method end it 'keeps protected methods protected' do - object.should_receive(:protected_method) + expect(object).to receive(:protected_method) expect(object.protected_methods).to include_method(:protected_method) object.public_method end diff --git a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb index e206d135d..b6709e23a 100644 --- a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb +++ b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb @@ -36,14 +36,14 @@ def existing_method # # end it "fails when expected message is not received" do - obj.should_receive(:msg) + expect(obj).to receive(:msg) expect { verify obj }.to raise_error(RSpec::Mocks::MockExpectationError) end it "fails when message is received with incorrect args" do - obj.should_receive(:msg).with(:correct_arg) + expect(obj).to receive(:msg).with(:correct_arg) expect { obj.msg(:incorrect_arg) }.to raise_error(RSpec::Mocks::MockExpectationError) @@ -51,20 +51,20 @@ def existing_method end it "passes when expected message is received" do - obj.should_receive(:msg) + expect(obj).to receive(:msg) obj.msg verify obj end it "passes when message is received with correct args" do - obj.should_receive(:msg).with(:correct_arg) + expect(obj).to receive(:msg).with(:correct_arg) obj.msg(:correct_arg) verify obj end it "restores the original method if it existed" do expect(obj.existing_method).to equal(:original_value) - obj.should_receive(:existing_method).and_return(:mock_value) + expect(obj).to receive(:existing_method).and_return(:mock_value) expect(obj.existing_method).to equal(:mock_value) verify obj expect(obj.existing_method).to equal(:original_value) @@ -73,7 +73,7 @@ def existing_method context "with an instance method handled by method_missing" do it "restores the original behavior" do expect(obj.captured_by_method_missing).to eq("response generated by method missing") - obj.stub(:captured_by_method_missing) { "foo" } + allow(obj).to receive(:captured_by_method_missing) { "foo" } expect(obj.captured_by_method_missing).to eq("foo") reset obj expect(obj.captured_by_method_missing).to eq("response generated by method missing") @@ -83,7 +83,7 @@ def existing_method context "with a class method handled by method_missing" do it "restores the original behavior" do expect(klass.captured_by_method_missing).to eq("response generated by method missing") - klass.stub(:captured_by_method_missing) { "foo" } + allow(klass).to receive(:captured_by_method_missing) { "foo" } expect(klass.captured_by_method_missing).to eq("foo") reset klass expect(klass.captured_by_method_missing).to eq("response generated by method missing") diff --git a/spec/rspec/mocks/passing_argument_matchers_spec.rb b/spec/rspec/mocks/passing_argument_matchers_spec.rb index 4cd6c7349..6a3b33bc5 100644 --- a/spec/rspec/mocks/passing_argument_matchers_spec.rb +++ b/spec/rspec/mocks/passing_argument_matchers_spec.rb @@ -3,7 +3,7 @@ module Mocks describe "Passing argument matchers" do before(:each) do @double = double('double') - Kernel.stub(:warn) + allow(Kernel).to receive(:warn) end after(:each) do @@ -12,129 +12,129 @@ module Mocks context "handling argument matchers" do it "accepts true as boolean()" do - @double.should_receive(:random_call).with(boolean()) + expect(@double).to receive(:random_call).with(boolean()) @double.random_call(true) end it "accepts false as boolean()" do - @double.should_receive(:random_call).with(boolean()) + expect(@double).to receive(:random_call).with(boolean()) @double.random_call(false) end it "accepts fixnum as kind_of(Numeric)" do - @double.should_receive(:random_call).with(kind_of(Numeric)) + expect(@double).to receive(:random_call).with(kind_of(Numeric)) @double.random_call(1) end it "accepts float as an_instance_of(Numeric)" do - @double.should_receive(:random_call).with(kind_of(Numeric)) + expect(@double).to receive(:random_call).with(kind_of(Numeric)) @double.random_call(1.5) end it "accepts fixnum as instance_of(Fixnum)" do - @double.should_receive(:random_call).with(instance_of(Fixnum)) + expect(@double).to receive(:random_call).with(instance_of(Fixnum)) @double.random_call(1) end it "does NOT accept fixnum as instance_of(Numeric)" do - @double.should_not_receive(:random_call).with(instance_of(Numeric)) + expect(@double).not_to receive(:random_call).with(instance_of(Numeric)) @double.random_call(1) end it "does NOT accept float as instance_of(Numeric)" do - @double.should_not_receive(:random_call).with(instance_of(Numeric)) + expect(@double).not_to receive(:random_call).with(instance_of(Numeric)) @double.random_call(1.5) end it "accepts string as anything()" do - @double.should_receive(:random_call).with("a", anything(), "c") + expect(@double).to receive(:random_call).with("a", anything(), "c") @double.random_call("a", "whatever", "c") end it "matches duck type with one method" do - @double.should_receive(:random_call).with(duck_type(:length)) + expect(@double).to receive(:random_call).with(duck_type(:length)) @double.random_call([]) end it "matches duck type with two methods" do - @double.should_receive(:random_call).with(duck_type(:abs, :div)) + expect(@double).to receive(:random_call).with(duck_type(:abs, :div)) @double.random_call(1) end it "matches no args against any_args()" do - @double.should_receive(:random_call).with(any_args) + expect(@double).to receive(:random_call).with(any_args) @double.random_call() end it "matches one arg against any_args()" do - @double.should_receive(:random_call).with(any_args) + expect(@double).to receive(:random_call).with(any_args) @double.random_call("a string") end it "matches no args against no_args()" do - @double.should_receive(:random_call).with(no_args) + expect(@double).to receive(:random_call).with(no_args) @double.random_call() end it "matches hash with hash_including same hash" do - @double.should_receive(:random_call).with(hash_including(:a => 1)) + expect(@double).to receive(:random_call).with(hash_including(:a => 1)) @double.random_call(:a => 1) end it "matches array with array_including same array" do - @double.should_receive(:random_call).with(array_including(1,2)) + expect(@double).to receive(:random_call).with(array_including(1,2)) @double.random_call([1,2]) end it "matches any arbitrary object using #===" do matcher = double - matcher.should_receive(:===).with(4).and_return(true) + expect(matcher).to receive(:===).with(4).and_return(true) - @double.should_receive(:foo).with(matcher) + expect(@double).to receive(:foo).with(matcher) @double.foo(4) end end context "handling non-matcher arguments" do it "matches non special symbol (can be removed when deprecated symbols are removed)" do - @double.should_receive(:random_call).with(:some_symbol) + expect(@double).to receive(:random_call).with(:some_symbol) @double.random_call(:some_symbol) end it "matches string against regexp" do - @double.should_receive(:random_call).with(/bcd/) + expect(@double).to receive(:random_call).with(/bcd/) @double.random_call("abcde") end it "matches regexp against regexp" do - @double.should_receive(:random_call).with(/bcd/) + expect(@double).to receive(:random_call).with(/bcd/) @double.random_call(/bcd/) end it "matches against a hash submitted and received by value" do - @double.should_receive(:random_call).with(:a => "a", :b => "b") + expect(@double).to receive(:random_call).with(:a => "a", :b => "b") @double.random_call(:a => "a", :b => "b") end it "matches against a hash submitted by reference and received by value" do opts = {:a => "a", :b => "b"} - @double.should_receive(:random_call).with(opts) + expect(@double).to receive(:random_call).with(opts) @double.random_call(:a => "a", :b => "b") end it "matches against a hash submitted by value and received by reference" do opts = {:a => "a", :b => "b"} - @double.should_receive(:random_call).with(:a => "a", :b => "b") + expect(@double).to receive(:random_call).with(:a => "a", :b => "b") @double.random_call(opts) end it "matches a class against itself" do - @double.should_receive(:foo).with(Fixnum) + expect(@double).to receive(:foo).with(Fixnum) @double.foo(Fixnum) end it "matches a class against an instance of itself" do - @double.should_receive(:foo).with(Fixnum) + expect(@double).to receive(:foo).with(Fixnum) @double.foo(3) end end diff --git a/spec/rspec/mocks/precise_counts_spec.rb b/spec/rspec/mocks/precise_counts_spec.rb index ba167e78d..b72406fb1 100644 --- a/spec/rspec/mocks/precise_counts_spec.rb +++ b/spec/rspec/mocks/precise_counts_spec.rb @@ -6,7 +6,7 @@ module Mocks end it "fails when exactly n times method is called less than n times" do - @double.should_receive(:do_something).exactly(3).times + expect(@double).to receive(:do_something).exactly(3).times @double.do_something @double.do_something expect { @@ -15,7 +15,7 @@ module Mocks end it "fails fast when exactly n times method is called more than n times" do - @double.should_receive(:do_something).exactly(3).times + expect(@double).to receive(:do_something).exactly(3).times @double.do_something @double.do_something @double.do_something @@ -25,14 +25,14 @@ module Mocks end it "fails when exactly n times method is never called" do - @double.should_receive(:do_something).exactly(3).times + expect(@double).to receive(:do_something).exactly(3).times expect { verify @double }.to raise_error(RSpec::Mocks::MockExpectationError) end it "passes if exactly n times method is called exactly n times" do - @double.should_receive(:do_something).exactly(3).times + expect(@double).to receive(:do_something).exactly(3).times @double.do_something @double.do_something @double.do_something @@ -40,22 +40,22 @@ module Mocks end it "returns the value given by a block when the exactly once method is called" do - @double.should_receive(:to_s).exactly(:once) { "testing" } + expect(@double).to receive(:to_s).exactly(:once) { "testing" } expect(@double.to_s).to eq "testing" verify @double end it "passes mutiple calls with different args" do - @double.should_receive(:do_something).once.with(1) - @double.should_receive(:do_something).once.with(2) + expect(@double).to receive(:do_something).once.with(1) + expect(@double).to receive(:do_something).once.with(2) @double.do_something(1) @double.do_something(2) verify @double end it "passes multiple calls with different args and counts" do - @double.should_receive(:do_something).twice.with(1) - @double.should_receive(:do_something).once.with(2) + expect(@double).to receive(:do_something).twice.with(1) + expect(@double).to receive(:do_something).once.with(2) @double.do_something(1) @double.do_something(2) @double.do_something(1) diff --git a/spec/rspec/mocks/serialization_spec.rb b/spec/rspec/mocks/serialization_spec.rb index 3c39c7bfc..ae2a84d99 100644 --- a/spec/rspec/mocks/serialization_spec.rb +++ b/spec/rspec/mocks/serialization_spec.rb @@ -35,7 +35,7 @@ def self.without_yaml_loaded(&block) let(:serializable_object) { RSpec::Mocks::SerializableObject.new(7, "something") } def set_stub - serializable_object.stub(:bazz => 5) + allow(serializable_object).to receive_messages(:bazz => 5) end shared_examples_for 'normal YAML serialization' do diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index f124c3f39..5248950ad 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -9,8 +9,8 @@ module RSpec::Mocks it "verifies all mocks within" do verifies = [] - space.proxy_for(dbl_1).stub(:verify) { verifies << :dbl_1 } - space.proxy_for(dbl_2).stub(:verify) { verifies << :dbl_2 } + allow(space.proxy_for(dbl_1)).to receive(:verify) { verifies << :dbl_1 } + allow(space.proxy_for(dbl_2)).to receive(:verify) { verifies << :dbl_2 } space.verify_all @@ -41,8 +41,8 @@ def define_singleton_method_on_recorder_for(klass, name, &block) it "resets all mocks within" do resets = [] - space.proxy_for(dbl_1).stub(:reset) { resets << :dbl_1 } - space.proxy_for(dbl_2).stub(:reset) { resets << :dbl_2 } + allow(space.proxy_for(dbl_1)).to receive(:reset) { resets << :dbl_1 } + allow(space.proxy_for(dbl_2)).to receive(:reset) { resets << :dbl_2 } space.reset_all diff --git a/spec/rspec/mocks/stash_spec.rb b/spec/rspec/mocks/stash_spec.rb index 668a1b74f..df76ca131 100644 --- a/spec/rspec/mocks/stash_spec.rb +++ b/spec/rspec/mocks/stash_spec.rb @@ -10,8 +10,8 @@ def self.foo(arg) end it "keeps the original method intact after multiple expectations are added on the same method" do - klass.should_receive(:foo).with(:fizbaz).and_return(:wowwow) - klass.should_receive(:foo).with(:bazbar).and_return(:okay) + expect(klass).to receive(:foo).with(:fizbaz).and_return(:wowwow) + expect(klass).to receive(:foo).with(:bazbar).and_return(:okay) klass.foo(:fizbaz) klass.foo(:bazbar) @@ -30,7 +30,7 @@ class << self end end - klass.should_receive(:alternate_new) + expect(klass).to receive(:alternate_new) expect(klass.alternate_new).to be_nil verify klass diff --git a/spec/rspec/mocks/stub_chain_spec.rb b/spec/rspec/mocks/stub_chain_spec.rb index 7c30286fd..c491bed92 100644 --- a/spec/rspec/mocks/stub_chain_spec.rb +++ b/spec/rspec/mocks/stub_chain_spec.rb @@ -4,8 +4,8 @@ module Mocks let(:object) { Object.new } it 'does not get confused by symbol vs string usage for the messages' do - object.stub_chain(:foo, :bar => 1) - object.stub_chain("foo", :bazz => 2) + allow(object).to receive_message_chain(:foo, :bar => 1) + allow(object).to receive_message_chain("foo", :bazz => 2) expect(object.foo.bar).to eq(1) expect(object.foo.bazz).to eq(2) @@ -14,21 +14,21 @@ module Mocks context "with one method in chain" do context "using and_return" do it "returns expected value from chaining only one method call" do - object.stub_chain(:msg1).and_return(:return_value) + allow(object).to receive_message_chain(:msg1).and_return(:return_value) expect(object.msg1).to equal(:return_value) end end context "using a block" do it "returns the correct value" do - object.stub_chain(:msg1) { :return_value } + allow(object).to receive_message_chain(:msg1) { :return_value } expect(object.msg1).to equal(:return_value) end end context "using a hash" do it "returns the value of the key/value pair" do - object.stub_chain(:msg1 => :return_value) + allow(object).to receive_message_chain(:msg1 => :return_value) expect(object.msg1).to equal(:return_value) end end @@ -36,28 +36,28 @@ module Mocks context "with two methods in chain" do it "accepts any number of arguments to the stubbed messages in the chain" do - object.stub_chain(:msg1, :msg2).and_return(:return_value) + allow(object).to receive_message_chain(:msg1, :msg2).and_return(:return_value) expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value) end context "using and_return" do it "returns expected value from chaining two method calls" do - object.stub_chain(:msg1, :msg2).and_return(:return_value) + allow(object).to receive_message_chain(:msg1, :msg2).and_return(:return_value) expect(object.msg1.msg2).to equal(:return_value) end end context "using a block" do it "returns the correct value" do - object.stub_chain(:msg1, :msg2) { :return_value } + allow(object).to receive_message_chain(:msg1, :msg2) { :return_value } expect(object.msg1.msg2).to equal(:return_value) end end context "using a hash" do it "returns the value of the key/value pair" do - object.stub_chain(:msg1, :msg2 => :return_value) + allow(object).to receive_message_chain(:msg1, :msg2 => :return_value) expect(object.msg1.msg2).to equal(:return_value) end end @@ -66,35 +66,35 @@ module Mocks context "with four methods in chain" do context "using and_return" do it "returns expected value from chaining two method calls" do - object.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value) expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end end context "using a block" do it "returns the correct value" do - object.stub_chain(:msg1, :msg2, :msg3, :msg4) { :return_value } + allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4) { :return_value } expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end end context "using a hash" do it "returns the value of the key/value pair" do - object.stub_chain(:msg1, :msg2, :msg3, :msg4 => :return_value) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4 => :return_value) expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end end context "using a hash with a string key" do it "returns the value of the key/value pair" do - object.stub_chain("msg1.msg2.msg3.msg4" => :return_value) + allow(object).to receive_message_chain("msg1.msg2.msg3.msg4" => :return_value) expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end end end it "returns expected value from chaining four method calls" do - object.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value) expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end @@ -102,8 +102,8 @@ module Mocks context "using and_return" do context "starting with the same message" do it "returns expected value" do - object.stub_chain(:msg1, :msg2, :msg3).and_return(:first) - object.stub_chain(:msg1, :msg2, :msg4).and_return(:second) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3).and_return(:first) + allow(object).to receive_message_chain(:msg1, :msg2, :msg4).and_return(:second) expect(object.msg1.msg2.msg3).to equal(:first) expect(object.msg1.msg2.msg4).to equal(:second) @@ -112,8 +112,8 @@ module Mocks context "starting with the different messages" do it "returns expected value" do - object.stub_chain(:msg1, :msg2, :msg3).and_return(:first) - object.stub_chain(:msg4, :msg2, :msg3).and_return(:second) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3).and_return(:first) + allow(object).to receive_message_chain(:msg4, :msg2, :msg3).and_return(:second) expect(object.msg1.msg2.msg3).to equal(:first) expect(object.msg4.msg2.msg3).to equal(:second) @@ -124,8 +124,8 @@ module Mocks context "using => value" do context "starting with the same message" do it "returns expected value" do - object.stub_chain(:msg1, :msg2, :msg3 => :first) - object.stub_chain(:msg1, :msg2, :msg4 => :second) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3 => :first) + allow(object).to receive_message_chain(:msg1, :msg2, :msg4 => :second) expect(object.msg1.msg2.msg3).to equal(:first) expect(object.msg1.msg2.msg4).to equal(:second) @@ -134,8 +134,8 @@ module Mocks context "starting with different messages" do it "returns expected value" do - object.stub_chain(:msg1, :msg2, :msg3 => :first) - object.stub_chain(:msg4, :msg2, :msg3 => :second) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3 => :first) + allow(object).to receive_message_chain(:msg4, :msg2, :msg3 => :second) expect(object.msg1.msg2.msg3).to equal(:first) expect(object.msg4.msg2.msg3).to equal(:second) @@ -145,20 +145,20 @@ module Mocks end it "returns expected value when chain is a dot separated string, like stub_chain('msg1.msg2.msg3')" do - object.stub_chain("msg1.msg2.msg3").and_return(:return_value) + allow(object).to receive_message_chain("msg1.msg2.msg3").and_return(:return_value) expect(object.msg1.msg2.msg3).to equal(:return_value) end it "returns expected value from two chains with shared messages at the beginning" do - object.stub_chain(:msg1, :msg2, :msg3, :msg4).and_return(:first) - object.stub_chain(:msg1, :msg2, :msg3, :msg5).and_return(:second) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4).and_return(:first) + allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg5).and_return(:second) expect(object.msg1.msg2.msg3.msg4).to equal(:first) expect(object.msg1.msg2.msg3.msg5).to equal(:second) end it "handles private instance methods (like Object#select) in the middle of a chain" do - object.stub_chain(:msg1, :select, :msg3 => 'answer') + allow(object).to receive_message_chain(:msg1, :select, :msg3 => 'answer') expect(object.msg1.select.msg3).to eq 'answer' end end diff --git a/spec/rspec/mocks/stub_implementation_spec.rb b/spec/rspec/mocks/stub_implementation_spec.rb index 0e58b96bf..f9506bc3a 100644 --- a/spec/rspec/mocks/stub_implementation_spec.rb +++ b/spec/rspec/mocks/stub_implementation_spec.rb @@ -4,7 +4,7 @@ module Mocks describe "with no args" do it "execs the block when called" do obj = double() - obj.stub(:foo) { :bar } + allow(obj).to receive(:foo) { :bar } expect(obj.foo).to eq :bar end end @@ -12,7 +12,7 @@ module Mocks describe "with one arg" do it "execs the block with that arg when called" do obj = double() - obj.stub(:foo) {|given| given} + allow(obj).to receive(:foo) {|given| given} expect(obj.foo(:bar)).to eq :bar end end @@ -20,7 +20,7 @@ module Mocks describe "with variable args" do it "execs the block when called" do obj = double() - obj.stub(:foo) {|*given| given.first} + allow(obj).to receive(:foo) {|*given| given.first} expect(obj.foo(:bar)).to eq :bar end end @@ -31,7 +31,7 @@ module Mocks it "replaces the stubbed method with the original method" do obj = Object.new def obj.foo; :original; end - obj.stub(:foo) + allow(obj).to receive(:foo) obj.unstub(:foo) expect(obj.foo).to eq :original end @@ -39,8 +39,8 @@ def obj.foo; :original; end it "removes all stubs with the supplied method name" do obj = Object.new def obj.foo; :original; end - obj.stub(:foo).with(1) - obj.stub(:foo).with(2) + allow(obj).to receive(:foo).with(1) + allow(obj).to receive(:foo).with(2) obj.unstub(:foo) expect(obj.foo).to eq :original end @@ -48,9 +48,9 @@ def obj.foo; :original; end it "does not remove any expectations with the same method name" do obj = Object.new def obj.foo; :original; end - obj.should_receive(:foo).with(3).and_return(:three) - obj.stub(:foo).with(1) - obj.stub(:foo).with(2) + expect(obj).to receive(:foo).with(3).and_return(:three) + allow(obj).to receive(:foo).with(1) + allow(obj).to receive(:foo).with(2) obj.unstub(:foo) expect(obj.foo(3)).to eq :three end @@ -59,8 +59,8 @@ def obj.foo; :original; end parent = Class.new child = Class.new(parent) - parent.stub(:new) - child.stub(:new) + allow(parent).to receive(:new) + allow(child).to receive(:new) parent.unstub(:new) child.unstub(:new) diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 5f28bea3e..c790103f5 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -29,24 +29,24 @@ def existing_private_instance_method describe "using stub" do it "returns declared value when message is received" do - @instance.stub(:msg).and_return(:return_value) + allow(@instance).to receive(:msg).and_return(:return_value) expect(@instance.msg).to equal(:return_value) verify @instance end end it "instructs an instance to respond_to the message" do - @instance.stub(:msg) + allow(@instance).to receive(:msg) expect(@instance).to respond_to(:msg) end it "instructs a class object to respond_to the message" do - @class.stub(:msg) + allow(@class).to receive(:msg) expect(@class).to respond_to(:msg) end it "ignores when expected message is received with no args" do - @instance.stub(:msg) + allow(@instance).to receive(:msg) @instance.msg expect do verify @instance @@ -54,7 +54,7 @@ def existing_private_instance_method end it "ignores when message is received with args" do - @instance.stub(:msg) + allow(@instance).to receive(:msg) @instance.msg(:an_arg) expect do verify @instance @@ -62,28 +62,28 @@ def existing_private_instance_method end it "ignores when expected message is not received" do - @instance.stub(:msg) + allow(@instance).to receive(:msg) expect do verify @instance end.not_to raise_error end it "handles multiple stubbed methods" do - @instance.stub(:msg1 => 1, :msg2 => 2) + allow(@instance).to receive_messages(:msg1 => 1, :msg2 => 2) expect(@instance.msg1).to eq(1) expect(@instance.msg2).to eq(2) end describe "#rspec_reset" do it "removes stubbed methods that didn't exist" do - @instance.stub(:non_existent_method) + allow(@instance).to receive(:non_existent_method) reset @instance expect(@instance).not_to respond_to(:non_existent_method) end it "restores existing instance methods" do # See bug reports 8302 and 7805 - @instance.stub(:existing_instance_method) { :stub_value } + allow(@instance).to receive(:existing_instance_method) { :stub_value } reset @instance expect(@instance.existing_instance_method).to eq(:original_value) end @@ -100,7 +100,7 @@ def self.say_hello subclass.instance_variable_set(:@hello, "Hello") expect(subclass.say_hello).to eq("Hello") - klass.stub(:say_hello) { "Howdy" } + allow(klass).to receive(:say_hello) { "Howdy" } expect(subclass.say_hello).to eq("Howdy") reset klass @@ -109,28 +109,28 @@ def self.say_hello it "restores existing private instance methods" do # See bug reports 8302 and 7805 - @instance.stub(:existing_private_instance_method) { :stub_value } + allow(@instance).to receive(:existing_private_instance_method) { :stub_value } reset @instance expect(@instance.send(:existing_private_instance_method)).to eq(:original_value) end it "restores existing class methods" do # See bug reports 8302 and 7805 - @class.stub(:existing_class_method) { :stub_value } + allow(@class).to receive(:existing_class_method) { :stub_value } reset @class expect(@class.existing_class_method).to eq(:original_value) end it "restores existing private class methods" do # See bug reports 8302 and 7805 - @class.stub(:existing_private_class_method) { :stub_value } + allow(@class).to receive(:existing_private_class_method) { :stub_value } reset @class expect(@class.send(:existing_private_class_method)).to eq(:original_value) end it "does not remove existing methods that have been stubbed twice" do - @instance.stub(:existing_instance_method) - @instance.stub(:existing_instance_method) + allow(@instance).to receive(:existing_instance_method) + allow(@instance).to receive(:existing_instance_method) reset @instance @@ -150,7 +150,7 @@ class << self; public :hello; end; expect(mod.hello).to eq(:hello) - mod.stub(:hello) { :stub } + allow(mod).to receive(:hello) { :stub } reset mod expect(mod.hello).to eq(:hello) @@ -195,14 +195,14 @@ def non_prepended_method end it "returns values in order to consecutive calls" do - @instance.stub(:msg).and_return("1",2,:three) + allow(@instance).to receive(:msg).and_return("1",2,:three) expect(@instance.msg).to eq("1") expect(@instance.msg).to eq(2) expect(@instance.msg).to eq(:three) end it "keeps returning last value in consecutive calls" do - @instance.stub(:msg).and_return("1",2,:three) + allow(@instance).to receive(:msg).and_return("1",2,:three) expect(@instance.msg).to eq("1") expect(@instance.msg).to eq(2) expect(@instance.msg).to eq(:three) @@ -211,7 +211,7 @@ def non_prepended_method end it "yields a specified object" do - @instance.stub(:method_that_yields).and_yield(:yielded_obj) + allow(@instance).to receive(:method_that_yields).and_yield(:yielded_obj) current_value = :value_before @instance.method_that_yields {|val| current_value = val} expect(current_value).to eq :yielded_obj @@ -219,7 +219,7 @@ def non_prepended_method end it "yields multiple times with multiple calls to and_yield" do - @instance.stub(:method_that_yields_multiple_times).and_yield(:yielded_value). + allow(@instance).to receive(:method_that_yields_multiple_times).and_yield(:yielded_value). and_yield(:another_value) current_value = [] @instance.method_that_yields_multiple_times {|val| current_value << val} @@ -229,35 +229,35 @@ def non_prepended_method it "yields a specified object and return another specified object" do yielded_obj = double("my mock") - yielded_obj.should_receive(:foo).with(:bar) - @instance.stub(:method_that_yields_and_returns).and_yield(yielded_obj).and_return(:baz) + expect(yielded_obj).to receive(:foo).with(:bar) + allow(@instance).to receive(:method_that_yields_and_returns).and_yield(yielded_obj).and_return(:baz) expect(@instance.method_that_yields_and_returns { |o| o.foo :bar }).to eq :baz end it "throws when told to" do - @stub.stub(:something).and_throw(:up) + allow(@stub).to receive(:something).and_throw(:up) expect { @stub.something }.to throw_symbol(:up) end it "throws with argument when told to" do - @stub.stub(:something).and_throw(:up, 'high') + allow(@stub).to receive(:something).and_throw(:up, 'high') expect { @stub.something }.to throw_symbol(:up, 'high') end it "overrides a pre-existing method" do - @stub.stub(:existing_instance_method).and_return(:updated_stub_value) + allow(@stub).to receive(:existing_instance_method).and_return(:updated_stub_value) expect(@stub.existing_instance_method).to eq :updated_stub_value end it "overrides a pre-existing stub" do - @stub.stub(:foo) { 'bar' } - @stub.stub(:foo) { 'baz' } + allow(@stub).to receive(:foo) { 'bar' } + allow(@stub).to receive(:foo) { 'baz' } expect(@stub.foo).to eq 'baz' end it "allows a stub and an expectation" do - @stub.stub(:foo).with("bar") - @stub.should_receive(:foo).with("baz") + allow(@stub).to receive(:foo).with("bar") + expect(@stub).to receive(:foo).with("baz") @stub.foo("bar") @stub.foo("baz") end @@ -266,7 +266,7 @@ def non_prepended_method describe "A method stub with args" do before(:each) do @stub = Object.new - @stub.stub(:foo).with("bar") + allow(@stub).to receive(:foo).with("bar") end it "does not complain if not called" do @@ -289,13 +289,13 @@ def non_prepended_method end it "does not complain if also mocked w/ different args" do - @stub.should_receive(:foo).with("baz") + expect(@stub).to receive(:foo).with("baz") @stub.foo("bar") @stub.foo("baz") end it "complains if also mocked w/ different args AND called w/ a 3rd set of args" do - @stub.should_receive(:foo).with("baz") + expect(@stub).to receive(:foo).with("baz") @stub.foo("bar") @stub.foo("baz") expect { @@ -304,11 +304,11 @@ def non_prepended_method end it 'uses the correct stubbed response when responding to a mock expectation' do - @stub.stub(:bar) { 15 } - @stub.stub(:bar).with(:eighteen) { 18 } - @stub.stub(:bar).with(:thirteen) { 13 } + allow(@stub).to receive(:bar) { 15 } + allow(@stub).to receive(:bar).with(:eighteen) { 18 } + allow(@stub).to receive(:bar).with(:thirteen) { 13 } - @stub.should_receive(:bar).exactly(4).times + expect(@stub).to receive(:bar).exactly(4).times expect(@stub.bar(:blah)).to eq(15) expect(@stub.bar(:thirteen)).to eq(13) diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index aedec6bbd..04a9d14de 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -2,13 +2,13 @@ it "fails if message is not received after expectation is set" do double = double(:msg => nil) double.msg - double.should_receive(:msg) + expect(double).to receive(:msg) expect { verify double }.to raise_error(RSpec::Mocks::MockExpectationError) end it "outputs arguments of similar calls" do double = double('double', :foo => true) - double.should_receive(:foo).with('first') + expect(double).to receive(:foo).with('first') double.foo('second') double.foo('third') expect { @@ -20,31 +20,31 @@ context "with argument constraint on stub" do it "matches any args if no arg constraint set on expectation" do double = double("mock") - double.stub(:foo).with(3).and_return("stub") - double.should_receive(:foo).at_least(:once).and_return("expectation") + allow(double).to receive(:foo).with(3).and_return("stub") + expect(double).to receive(:foo).at_least(:once).and_return("expectation") double.foo verify double end it "matches specific args set on expectation" do double = double("mock") - double.stub(:foo).with(3).and_return("stub") - double.should_receive(:foo).at_least(:once).with(4).and_return("expectation") + allow(double).to receive(:foo).with(3).and_return("stub") + expect(double).to receive(:foo).at_least(:once).with(4).and_return("expectation") double.foo(4) verify double end it "fails if expectation's arg constraint is not met" do double = double("mock") - double.stub(:foo).with(3).and_return("stub") - double.should_receive(:foo).at_least(:once).with(4).and_return("expectation") + allow(double).to receive(:foo).with(3).and_return("stub") + expect(double).to receive(:foo).at_least(:once).with(4).and_return("expectation") double.foo(3) expect { verify double }.to raise_error(/expected: \(4\)\s+got: \(3\)/) end it 'distinguishes between individual values and arrays properly' do dbl = double - dbl.stub(:foo).with('a', ['b']) + allow(dbl).to receive(:foo).with('a', ['b']) expect { dbl.foo(['a'], 'b') diff --git a/spec/rspec/mocks/to_ary_spec.rb b/spec/rspec/mocks/to_ary_spec.rb index f1c0d68e3..3a28a2aee 100644 --- a/spec/rspec/mocks/to_ary_spec.rb +++ b/spec/rspec/mocks/to_ary_spec.rb @@ -1,12 +1,12 @@ describe "a double receiving to_ary" do shared_examples "to_ary" do it "can be overridden with a stub" do - obj.stub(:to_ary) { :non_nil_value } + allow(obj).to receive(:to_ary) { :non_nil_value } expect(obj.to_ary).to be(:non_nil_value) end it "responds when overriden" do - obj.stub(:to_ary) { :non_nil_value } + allow(obj).to receive(:to_ary) { :non_nil_value } expect(obj).to respond_to(:to_ary) end diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index cbace3842..ed454045f 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -6,28 +6,28 @@ module Mocks end it "passes when called twice" do - @double.should_receive(:do_something).twice + expect(@double).to receive(:do_something).twice @double.do_something @double.do_something verify @double end it "passes when called twice with specified args" do - @double.should_receive(:do_something).twice.with("1", 1) + expect(@double).to receive(:do_something).twice.with("1", 1) @double.do_something("1", 1) @double.do_something("1", 1) verify @double end it "passes when called twice with unspecified args" do - @double.should_receive(:do_something).twice + expect(@double).to receive(:do_something).twice @double.do_something("1") @double.do_something(1) verify @double end it "fails fast when call count is higher than expected" do - @double.should_receive(:do_something).twice + expect(@double).to receive(:do_something).twice @double.do_something @double.do_something expect { @@ -36,7 +36,7 @@ module Mocks end it "fails when call count is lower than expected" do - @double.should_receive(:do_something).twice + expect(@double).to receive(:do_something).twice @double.do_something expect { verify @double @@ -44,7 +44,7 @@ module Mocks end it "fails when called wrong args on the first call" do - @double.should_receive(:do_something).twice.with("1", 1) + expect(@double).to receive(:do_something).twice.with("1", 1) expect { @double.do_something(1, "1") }.to raise_error(RSpec::Mocks::MockExpectationError) @@ -52,7 +52,7 @@ module Mocks end it "fails when called with wrong args on the second call" do - @double.should_receive(:do_something).twice.with("1", 1) + expect(@double).to receive(:do_something).twice.with("1", 1) @double.do_something("1", 1) expect { @double.do_something(1, "1") diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index f2ee55c27..2ed48248d 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -78,7 +78,7 @@ module Mocks it 'allows any instance method to be stubbed' do o = instance_double('NonloadedClass') - o.stub(:undefined_instance_method).with(:arg).and_return(true) + allow(o).to receive(:undefined_instance_method).with(:arg).and_return(true) expect(o.undefined_instance_method(:arg)).to eq(true) end @@ -125,8 +125,8 @@ module Mocks o = instance_double('LoadedClass', :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) - prevents { o.stub(:undefined_instance_method) } - prevents { o.stub(:defined_class_method) } + prevents { allow(o).to receive(:undefined_instance_method) } + prevents { allow(o).to receive(:defined_class_method) } end it 'only allows instance methods that exist to be expected' do @@ -136,8 +136,8 @@ module Mocks prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_class_method) } - prevents { o.should_receive(:undefined_instance_method) } - prevents { o.should_receive(:defined_class_method) } + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_class_method) } end it 'allows `send` to be stubbed if it is defined on the class' do @@ -352,8 +352,8 @@ def use; end o = class_double('LoadedClass', :defined_class_method => 1) expect(o.defined_class_method).to eq(1) - prevents { o.stub(:undefined_instance_method) } - prevents { o.stub(:defined_instance_method) } + prevents { allow(o).to receive(:undefined_instance_method) } + prevents { allow(o).to receive(:defined_instance_method) } end it 'only allows class methods that exist to be expected' do @@ -363,8 +363,8 @@ def use; end prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_instance_method) } - prevents { o.should_receive(:undefined_instance_method) } - prevents { o.should_receive(:defined_instance_method) } + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_instance_method) } end it 'gives a descriptive error message for NoMethodError' do diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 4d8c4ea6c..1d095828d 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -2,7 +2,7 @@ describe ".verify" do it "delegates to the space" do foo = double - foo.should_receive(:bar) + expect(foo).to receive(:bar) expect do RSpec::Mocks.verify end.to raise_error(RSpec::Mocks::MockExpectationError) From e4800a34bff1882ca22d5b1dab3c8b66d8135f06 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 26 Feb 2014 17:41:50 -0800 Subject: [PATCH 0522/1179] Fix any_instance specs after transpec has updated them. - Some specs only apply to the old :should syntax. - Some specs were testing invalid things that transpec removed. --- spec/rspec/mocks/any_instance_spec.rb | 92 +++++-------- spec/rspec/mocks/should_syntax_spec.rb | 183 +++++++++++++++++++++++++ 2 files changed, 215 insertions(+), 60 deletions(-) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 07c5b2dfd..35df43144 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -17,11 +17,7 @@ def private_method; :private_method_return_value; end let(:existing_method_return_value){ :existing_method_return_value } context "invocation order" do - context "#stub" do - it "raises an error if 'stub' follows 'with'" do - expect { allow(klass.any_instance.with("1")).to receive(:foo) }.to raise_error(NoMethodError) - end - + context "when stubbing" do it "raises an error if 'with' follows 'and_return'" do expect { allow_any_instance_of(klass).to receive(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) end @@ -35,17 +31,7 @@ def private_method; :private_method_return_value; end end end - context "#stub_chain" do - it "raises an error if 'stub_chain' follows 'and_return'" do - expect { allow(klass.any_instance.and_return("1")).to receive_message_chain(:foo, :bar) }.to raise_error(NoMethodError) - end - end - - context "#should_receive" do - it "raises an error if 'should_receive' follows 'with'" do - expect { expect(klass.any_instance.with("1")).to receive(:foo) }.to raise_error(NoMethodError) - end - + context "when setting a message expectation" do it "raises an error if 'with' follows 'and_return'" do pending "see Github issue #42" expect { expect_any_instance_of(klass).to receive(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) @@ -58,7 +44,7 @@ def private_method; :private_method_return_value; end end end - context "with #stub" do + context "when stubbing" do it "does not suppress an exception when a method that doesn't exist is invoked" do allow_any_instance_of(klass).to receive(:foo) expect { klass.new.bar }.to raise_error(NoMethodError) @@ -72,23 +58,18 @@ def private_method; :private_method_return_value; end expect(instance.bar).to eq('bar') end - it "adheres to the contract of multiple method stubbing withou any instance" do - expect(allow(Object.new).to receive_messages(:foo => 'foo', :bar => 'bar')).to eq(:foo => 'foo', :bar => 'bar') - expect(allow_any_instance_of(klass).to receive_messages(:foo => 'foo', :bar => 'bar')).to eq(:foo => 'foo', :bar => 'bar') - end - - context "allows a chain of methods to be stubbed using #stub_chain" do - it "given symbols representing the methods" do + context "allows a chain of methods to be stubbed using #receive_message_chain" do + example "given symbols representing the methods" do allow_any_instance_of(klass).to receive_message_chain(:one, :two, :three).and_return(:four) expect(klass.new.one.two.three).to eq(:four) end - it "given a hash as the last argument uses the value as the expected return value" do + example "given a hash as the last argument uses the value as the expected return value" do allow_any_instance_of(klass).to receive_message_chain(:one, :two, :three => :four) expect(klass.new.one.two.three).to eq(:four) end - it "given a string of '.' separated method names representing the chain" do + example "given a string of '.' separated method names representing the chain" do allow_any_instance_of(klass).to receive_message_chain('one.two.three').and_return(:four) expect(klass.new.one.two.three).to eq(:four) end @@ -127,7 +108,7 @@ def private_method; :private_method_return_value; end it 'handles method restoration on subclasses' do allow_any_instance_of(super_class).to receive(:foo) allow_any_instance_of(sub_class).to receive(:foo) - sub_class.any_instance.unstub(:foo) + allow_any_instance_of(sub_class).to receive(:foo).and_call_original expect(sub_class.new.foo).to eq("bar") end end @@ -170,12 +151,12 @@ def private_method; :private_method_return_value; end end context "with #and_return" do - it "stubs a method that doesn't exist" do + it "can stub a method that doesn't exist" do allow_any_instance_of(klass).to receive(:foo).and_return(1) expect(klass.new.foo).to eq(1) end - it "stubs a method that exists" do + it "can stub a method that exists" do allow_any_instance_of(klass).to receive(:existing_method).and_return(1) expect(klass.new.existing_method).to eq(1) end @@ -192,7 +173,7 @@ def private_method; :private_method_return_value; end it "yields the value specified" do yielded_value = Object.new allow_any_instance_of(klass).to receive(:foo).and_yield(yielded_value) - klass.new.foo{|value| expect(value).to be(yielded_value)} + expect { |b| klass.new.foo(&b) }.to yield_with_args(yielded_value) end end @@ -217,14 +198,14 @@ def private_method; :private_method_return_value; end end context "with #and_raise" do - it "stubs a method that doesn't exist" do + it "can stub a method that doesn't exist" do allow_any_instance_of(klass).to receive(:foo).and_raise(CustomErrorForAnyInstanceSpec) - expect { klass.new.foo}.to raise_error(CustomErrorForAnyInstanceSpec) + expect { klass.new.foo }.to raise_error(CustomErrorForAnyInstanceSpec) end - it "stubs a method that exists" do + it "can stub a method that exists" do allow_any_instance_of(klass).to receive(:existing_method).and_raise(CustomErrorForAnyInstanceSpec) - expect { klass.new.existing_method}.to raise_error(CustomErrorForAnyInstanceSpec) + expect { klass.new.existing_method }.to raise_error(CustomErrorForAnyInstanceSpec) end end @@ -298,17 +279,17 @@ class RSpec::SampleRspecTestClass;end end end - context "unstub implementation" do + context "unstubbing using `and_call_original`" do it "replaces the stubbed method with the original method" do allow_any_instance_of(klass).to receive(:existing_method) - klass.any_instance.unstub(:existing_method) + allow_any_instance_of(klass).to receive(:existing_method).and_call_original expect(klass.new.existing_method).to eq(:existing_method_return_value) end it "removes all stubs with the supplied method name" do allow_any_instance_of(klass).to receive(:existing_method).with(1) allow_any_instance_of(klass).to receive(:existing_method).with(2) - klass.any_instance.unstub(:existing_method) + allow_any_instance_of(klass).to receive(:existing_method).and_call_original expect(klass.new.existing_method).to eq(:existing_method_return_value) end @@ -316,7 +297,9 @@ class RSpec::SampleRspecTestClass;end allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = klass.new obj.existing_method - klass.any_instance.unstub(:existing_method) + allow_any_instance_of(klass).to receive(:existing_method).and_call_original + + pending "not working for `and_call_original` yet, but works with `unstub`" expect(obj.existing_method).to eq(:existing_method_return_value) end @@ -324,7 +307,9 @@ class RSpec::SampleRspecTestClass;end allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = Class.new(klass).new expect(obj.existing_method).to eq(:any_instance_value) - klass.any_instance.unstub(:existing_method) + allow_any_instance_of(klass).to receive(:existing_method).and_call_original + + pending "not working for `and_call_original` yet, but works with `unstub`" expect(obj.existing_method).to eq(:existing_method_return_value) end @@ -332,7 +317,7 @@ class RSpec::SampleRspecTestClass;end allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = klass.new allow(obj).to receive(:existing_method).and_return(:local_method) - klass.any_instance.unstub(:existing_method) + allow_any_instance_of(klass).to receive(:existing_method).and_call_original expect(obj.existing_method).to eq(:local_method) end @@ -340,24 +325,18 @@ class RSpec::SampleRspecTestClass;end expect_any_instance_of(klass).to receive(:existing_method_with_arguments).with(3).and_return(:three) allow_any_instance_of(klass).to receive(:existing_method_with_arguments).with(1) allow_any_instance_of(klass).to receive(:existing_method_with_arguments).with(2) - klass.any_instance.unstub(:existing_method_with_arguments) + allow_any_instance_of(klass).to receive(:existing_method_with_arguments).and_call_original expect(klass.new.existing_method_with_arguments(3)).to eq(:three) end - it "raises a MockExpectationError if the method has not been stubbed" do - expect { - klass.any_instance.unstub(:existing_method) - }.to raise_error(RSpec::Mocks::MockExpectationError, 'The method `existing_method` was not stubbed or was already unstubbed') - end - it 'does not get confused about string vs symbol usage for the message' do allow_any_instance_of(klass).to receive(:existing_method) { :stubbed } - klass.any_instance.unstub("existing_method") + allow_any_instance_of(klass).to receive("existing_method").and_call_original expect(klass.new.existing_method).to eq(:existing_method_return_value) end end - context "with #should_not_receive" do + context "expect_any_instance_of(...).not_to receive" do it "fails if the method is called" do expect_any_instance_of(klass).not_to receive(:existing_method) expect { klass.new.existing_method }.to raise_error(RSpec::Mocks::MockExpectationError) @@ -402,7 +381,7 @@ class RSpec::SampleRspecTestClass;end end end - context "with #should_receive" do + context "setting a message expectation" do let(:foo_expectation_error_message) { 'Exactly one instance should have received the following message(s) but didn\'t: foo' } let(:existing_method_expectation_error_message) { 'Exactly one instance should have received the following message(s) but didn\'t: existing_method' } @@ -475,7 +454,6 @@ class RSpec::SampleRspecTestClass;end end) end - it "pass when expectations are met" do expect_any_instance_of(klass).to receive(:foo) expect(klass).to receive(:woot).and_return(result = Object.new) @@ -752,7 +730,7 @@ def foo; end context "existing method" do before(:each) do - klass.any_instance # to force it to be tracked + RSpec::Mocks.space.any_instance_recorder_for(klass) # to force it to be tracked end context "with stubbing" do @@ -882,12 +860,6 @@ def foo; end end end - it "adds an class to the current space when #any_instance is invoked" do - expect { - klass.any_instance - }.to change { space.any_instance_recorders.size }.by(1) - end - it "adds an instance to the current space when stubbed method is invoked" do allow_any_instance_of(klass).to receive(:foo) instance = klass.new @@ -982,7 +954,7 @@ def call(*args) klass = Class.new do undef_method :dup end - klass.any_instance + allow_any_instance_of(klass).to receive(:foo) end it "doesn't fail when dup accepts parameters" do @@ -991,7 +963,7 @@ def dup(funky_option) end end - klass.any_instance + allow_any_instance_of(klass).to receive(:foo) expect { klass.new.dup('Dup dup dup') }.to_not raise_error end diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index 98d76f989..40e06f058 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -50,6 +50,189 @@ expect(Object.new.should_not_receive(:foobar)).to be_negative end end + + describe "#any_instance" do + let(:klass) do + Class.new do + def existing_method; :existing_method_return_value; end + def existing_method_with_arguments(arg_one, arg_two = nil); :existing_method_with_arguments_return_value; end + def another_existing_method; end + private + def private_method; :private_method_return_value; end + end + end + + it "adds an class to the current space" do + expect { + klass.any_instance + }.to change { RSpec::Mocks.space.any_instance_recorders.size }.by(1) + end + + context "invocation order" do + describe "#stub" do + it "raises an error if 'stub' follows 'with'" do + expect { klass.any_instance.with("1").stub(:foo) }.to raise_error(NoMethodError) + end + + it "raises an error if 'with' follows 'and_return'" do + expect { klass.any_instance.stub(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) + end + + it "raises an error if 'with' follows 'and_raise'" do + expect { klass.any_instance.stub(:foo).and_raise(1).with("1") }.to raise_error(NoMethodError) + end + + it "raises an error if 'with' follows 'and_yield'" do + expect { klass.any_instance.stub(:foo).and_yield(1).with("1") }.to raise_error(NoMethodError) + end + + context "behaves as 'every instance'" do + let(:super_class) { Class.new { def foo; 'bar'; end } } + let(:sub_class) { Class.new(super_class) } + + it 'handles `unstub` on subclasses' do + super_class.any_instance.stub(:foo) + sub_class.any_instance.stub(:foo) + sub_class.any_instance.unstub(:foo) + expect(sub_class.new.foo).to eq("bar") + end + end + end + + describe "#stub_chain" do + it "raises an error if 'stub_chain' follows 'and_return'" do + expect { klass.any_instance.and_return("1").stub_chain(:foo, :bar) }.to raise_error(NoMethodError) + end + + context "allows a chain of methods to be stubbed using #stub_chain" do + example "given symbols representing the methods" do + klass.any_instance.stub_chain(:one, :two, :three).and_return(:four) + expect(klass.new.one.two.three).to eq(:four) + end + + example "given a hash as the last argument uses the value as the expected return value" do + klass.any_instance.stub_chain(:one, :two, :three => :four) + expect(klass.new.one.two.three).to eq(:four) + end + + example "given a string of '.' separated method names representing the chain" do + klass.any_instance.stub_chain('one.two.three').and_return(:four) + expect(klass.new.one.two.three).to eq(:four) + end + end + end + + describe "#should_receive" do + it "raises an error if 'should_receive' follows 'with'" do + expect { klass.any_instance.with("1").should_receive(:foo) }.to raise_error(NoMethodError) + end + end + + describe "#should_not_receive" do + it "fails if the method is called" do + klass.any_instance.should_not_receive(:existing_method) + expect { klass.new.existing_method }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "passes if no method is called" do + expect { klass.any_instance.should_not_receive(:existing_method) }.to_not raise_error + end + + it "passes if only a different method is called" do + klass.any_instance.should_not_receive(:existing_method) + expect { klass.new.another_existing_method }.to_not raise_error + end + + context "with constraints" do + it "fails if the method is called with the specified parameters" do + klass.any_instance.should_not_receive(:existing_method_with_arguments).with(:argument_one, :argument_two) + expect { + klass.new.existing_method_with_arguments(:argument_one, :argument_two) + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "passes if the method is called with different parameters" do + klass.any_instance.should_not_receive(:existing_method_with_arguments).with(:argument_one, :argument_two) + expect { klass.new.existing_method_with_arguments(:argument_three, :argument_four) }.to_not raise_error + end + end + + context 'when used in combination with should_receive' do + it 'passes if only the expected message is received' do + klass.any_instance.should_receive(:foo) + klass.any_instance.should_not_receive(:bar) + klass.new.foo + verify_all + end + end + + it "prevents confusing double-negative expressions involving `never`" do + expect { + klass.any_instance.should_not_receive(:not_expected).never + }.to raise_error(/trying to negate it again/) + end + end + + describe "#unstub" do + it "replaces the stubbed method with the original method" do + klass.any_instance.stub(:existing_method) + klass.any_instance.unstub(:existing_method) + expect(klass.new.existing_method).to eq(:existing_method_return_value) + end + + it "removes all stubs with the supplied method name" do + klass.any_instance.stub(:existing_method).with(1) + klass.any_instance.stub(:existing_method).with(2) + klass.any_instance.unstub(:existing_method) + expect(klass.new.existing_method).to eq(:existing_method_return_value) + end + + it "removes stubs even if they have already been invoked" do + klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + obj = klass.new + obj.existing_method + klass.any_instance.unstub(:existing_method) + expect(obj.existing_method).to eq(:existing_method_return_value) + end + + it "removes stubs from sub class after invokation when super class was originally stubbed" do + klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + obj = Class.new(klass).new + expect(obj.existing_method).to eq(:any_instance_value) + klass.any_instance.unstub(:existing_method) + expect(obj.existing_method).to eq(:existing_method_return_value) + end + + it "does not remove any stubs set directly on an instance" do + klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + obj = klass.new + obj.stub(:existing_method).and_return(:local_method) + klass.any_instance.unstub(:existing_method) + expect(obj.existing_method).to eq(:local_method) + end + + it "does not remove any expectations with the same method name" do + klass.any_instance.should_receive(:existing_method_with_arguments).with(3).and_return(:three) + klass.any_instance.stub(:existing_method_with_arguments).with(1) + klass.any_instance.stub(:existing_method_with_arguments).with(2) + klass.any_instance.unstub(:existing_method_with_arguments) + expect(klass.new.existing_method_with_arguments(3)).to eq(:three) + end + + it "raises a MockExpectationError if the method has not been stubbed" do + expect { + klass.any_instance.unstub(:existing_method) + }.to raise_error(RSpec::Mocks::MockExpectationError, 'The method `existing_method` was not stubbed or was already unstubbed') + end + + it 'does not get confused about string vs symbol usage for the message' do + klass.any_instance.stub(:existing_method) { :stubbed } + klass.any_instance.unstub("existing_method") + expect(klass.new.existing_method).to eq(:existing_method_return_value) + end + end + end + end end RSpec.context "with default syntax configuration" do From 6cb45cee1ac0313211f213edc48d8cc8ebc7d363 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 26 Feb 2014 22:39:08 -0800 Subject: [PATCH 0523/1179] Fix a use of `stub` that was missed by transpec. --- spec/rspec/mocks/block_return_value_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 76f067ad6..9315ee35a 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -27,7 +27,7 @@ it 'forwards all given args to the block, even when it receives a block' do obj = Object.new yielded_args = [] - eval("obj.stub(:foo) { |*args, &bl| yielded_args << args }") + allow(obj).to receive(:foo) { |*args, &bl| yielded_args << args } obj.foo(1, 2, 3) expect(yielded_args).to eq([[1, 2, 3]]) From 1fbfdd8dadf785138f648d4ef348f915ef6c48da Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 26 Feb 2014 21:41:06 -0800 Subject: [PATCH 0524/1179] Migrate some more specs that are `should`-specific. --- spec/rspec/mocks/before_all_spec.rb | 63 +--------- spec/rspec/mocks/record_messages_spec.rb | 24 ---- spec/rspec/mocks/should_syntax_spec.rb | 112 ++++++++++++++++++ spec/rspec/mocks/stub_implementation_spec.rb | 21 ++-- .../before_all_shared_example_group.rb | 16 +++ 5 files changed, 139 insertions(+), 97 deletions(-) delete mode 100644 spec/rspec/mocks/record_messages_spec.rb create mode 100644 spec/support/before_all_shared_example_group.rb diff --git a/spec/rspec/mocks/before_all_spec.rb b/spec/rspec/mocks/before_all_spec.rb index 2fa4eea58..4c1e47546 100644 --- a/spec/rspec/mocks/before_all_spec.rb +++ b/spec/rspec/mocks/before_all_spec.rb @@ -1,21 +1,6 @@ -describe "Using rspec-mocks features in before(:all) blocks" do - shared_examples_for "fails in a before(:all) block" do - the_error = nil - before(:all) do - begin - use_rspec_mocks - rescue - the_error = $! - end - end - - it "raises an error with a useful message" do - expect(the_error).to be_a_kind_of(RSpec::Mocks::OutsideOfExampleError) - - expect(the_error.message).to match(/The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported./) - end - end +require 'support/before_all_shared_example_group' +describe "Using rspec-mocks features in before(:all) blocks" do describe "#stub_const" do include_examples "fails in a before(:all) block" do def use_rspec_mocks @@ -48,50 +33,10 @@ def use_rspec_mocks end end - describe "#stub" do - include_examples "fails in a before(:all) block" do - def use_rspec_mocks - allow(Object).to receive(:foo) - end - end - end - - describe "#unstub" do - include_examples "fails in a before(:all) block" do - def use_rspec_mocks - Object.unstub(:foo) - end - end - end - - describe "#should_receive" do - include_examples "fails in a before(:all) block" do - def use_rspec_mocks - expect(Object).to receive(:foo) - end - end - end - - describe "#should_not_receive" do - include_examples "fails in a before(:all) block" do - def use_rspec_mocks - expect(Object).not_to receive(:foo) - end - end - end - - describe "#any_instance" do - include_examples "fails in a before(:all) block" do - def use_rspec_mocks - expect_any_instance_of(Object).to receive(:foo) - end - end - end - - describe "#stub_chain" do + describe "allow(...).to receive_message_chain" do include_examples "fails in a before(:all) block" do def use_rspec_mocks - allow(Object).to receive_message_chain(:foo) + allow(Object).to receive_message_chain(:foo, :bar) end end end diff --git a/spec/rspec/mocks/record_messages_spec.rb b/spec/rspec/mocks/record_messages_spec.rb deleted file mode 100644 index 0a59b2d97..000000000 --- a/spec/rspec/mocks/record_messages_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -module RSpec - module Mocks - describe "a mock" do - before(:each) do - @mock = double("mock").as_null_object - end - it "answers false for received_message? when no messages received" do - expect(@mock.received_message?(:message)).to be_falsey - end - it "answers true for received_message? when message received" do - @mock.message - expect(@mock.received_message?(:message)).to be_truthy - end - it "answers true for received_message? when message received with correct args" do - @mock.message 1,2,3 - expect(@mock.received_message?(:message, 1,2,3)).to be_truthy - end - it "answers false for received_message? when message received with incorrect args" do - @mock.message 1,2,3 - expect(@mock.received_message?(:message, 1,2)).to be_falsey - end - end - end -end diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index 40e06f058..a6ae70c24 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -1,6 +1,31 @@ +require 'support/before_all_shared_example_group' + RSpec.describe "Using the legacy should syntax" do include_context "with syntax", [:should, :expect] + describe "#received_message?" do + let(:dbl) { double("double").as_null_object } + + it "answers false for received_message? when no messages received" do + expect(dbl.received_message?(:message)).to be_falsey + end + + it "answers true for received_message? when message received" do + dbl.message + expect(dbl.received_message?(:message)).to be_truthy + end + + it "answers true for received_message? when message received with correct args" do + dbl.message 1,2,3 + expect(dbl.received_message?(:message, 1,2,3)).to be_truthy + end + + it "answers false for received_message? when message received with incorrect args" do + dbl.message 1,2,3 + expect(dbl.received_message?(:message, 1,2)).to be_falsey + end + end + describe "#stub" do it "supports options" do double.stub(:foo, :expected_from => "bar") @@ -11,9 +36,84 @@ expect(double.stub(:foo).and_raise("boom")).to be_nil expect(double.stub(:foo).and_throw(:foo)).to be_nil end + + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + Object.stub(:foo) + end + end + end + + describe "#stub_chain" do + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + Object.stub_chain(:foo, :bar) + end + end + end + + describe "#unstub" do + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + Object.unstub(:foo) + end + end + + it "replaces the stubbed method with the original method" do + obj = Object.new + def obj.foo; :original; end + obj.stub(:foo) + obj.unstub(:foo) + expect(obj.foo).to eq :original + end + + it "removes all stubs with the supplied method name" do + obj = Object.new + def obj.foo; :original; end + obj.stub(:foo).with(1) + obj.stub(:foo).with(2) + obj.unstub(:foo) + expect(obj.foo).to eq :original + end + + it "does not remove any expectations with the same method name" do + obj = Object.new + def obj.foo; :original; end + obj.should_receive(:foo).with(3).and_return(:three) + obj.stub(:foo).with(1) + obj.stub(:foo).with(2) + obj.unstub(:foo) + expect(obj.foo(3)).to eq :three + end + + it "restores the correct implementations when stubbed and unstubbed on a parent and child class" do + parent = Class.new + child = Class.new(parent) + + parent.stub(:new) + child.stub(:new) + parent.unstub(:new) + child.unstub(:new) + + expect(parent.new).to be_an_instance_of parent + expect(child.new).to be_an_instance_of child + end + + it "raises a MockExpectationError if the method has not been stubbed" do + obj = Object.new + expect { + obj.unstub(:foo) + }.to raise_error(RSpec::Mocks::MockExpectationError) + end end describe "#should_receive" do + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + Object.should_receive(:foo) + end + end + context "with an options hash" do it "reports the file and line submitted with :expected_from" do begin @@ -49,6 +149,12 @@ it "returns a negative message expectation" do expect(Object.new.should_not_receive(:foobar)).to be_negative end + + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + Object.should_not_receive(:foo) + end + end end describe "#any_instance" do @@ -62,6 +168,12 @@ def private_method; :private_method_return_value; end end end + include_examples "fails in a before(:all) block" do + def use_rspec_mocks + Object.any_instance.should_receive(:foo) + end + end + it "adds an class to the current space" do expect { klass.any_instance diff --git a/spec/rspec/mocks/stub_implementation_spec.rb b/spec/rspec/mocks/stub_implementation_spec.rb index f9506bc3a..32e3b7484 100644 --- a/spec/rspec/mocks/stub_implementation_spec.rb +++ b/spec/rspec/mocks/stub_implementation_spec.rb @@ -26,13 +26,12 @@ module Mocks end end - - describe "unstub implementation" do + describe "unstubbing with `and_call_original`" do it "replaces the stubbed method with the original method" do obj = Object.new def obj.foo; :original; end allow(obj).to receive(:foo) - obj.unstub(:foo) + allow(obj).to receive(:foo).and_call_original expect(obj.foo).to eq :original end @@ -41,7 +40,7 @@ def obj.foo; :original; end def obj.foo; :original; end allow(obj).to receive(:foo).with(1) allow(obj).to receive(:foo).with(2) - obj.unstub(:foo) + allow(obj).to receive(:foo).and_call_original expect(obj.foo).to eq :original end @@ -51,7 +50,7 @@ def obj.foo; :original; end expect(obj).to receive(:foo).with(3).and_return(:three) allow(obj).to receive(:foo).with(1) allow(obj).to receive(:foo).with(2) - obj.unstub(:foo) + allow(obj).to receive(:foo).and_call_original expect(obj.foo(3)).to eq :three end @@ -61,19 +60,13 @@ def obj.foo; :original; end allow(parent).to receive(:new) allow(child).to receive(:new) - parent.unstub(:new) - child.unstub(:new) + allow(parent).to receive(:new).and_call_original + allow(child).to receive(:new).and_call_original expect(parent.new).to be_an_instance_of parent + pending "not working for `and_call_original` yet, but works with `unstub`" expect(child.new).to be_an_instance_of child end - - it "raises a MockExpectationError if the method has not been stubbed" do - obj = Object.new - expect { - obj.unstub(:foo) - }.to raise_error(RSpec::Mocks::MockExpectationError) - end end end end diff --git a/spec/support/before_all_shared_example_group.rb b/spec/support/before_all_shared_example_group.rb new file mode 100644 index 000000000..e9b6a8f85 --- /dev/null +++ b/spec/support/before_all_shared_example_group.rb @@ -0,0 +1,16 @@ +shared_examples_for "fails in a before(:all) block" do + the_error = nil + before(:all) do + begin + use_rspec_mocks + rescue + the_error = $! + end + end + + it "raises an error with a useful message" do + expect(the_error).to be_a_kind_of(RSpec::Mocks::OutsideOfExampleError) + + expect(the_error.message).to match(/The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported./) + end +end From cb2e368131e7fc481624aa6fabef5363578b6c96 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 27 Feb 2014 00:01:07 -0800 Subject: [PATCH 0525/1179] Disable :should syntax by default. Fixes #600. --- spec/rspec/mocks/configuration_spec.rb | 6 ++++++ spec/spec_helper.rb | 6 ++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 4d31db320..6a69c185f 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -34,6 +34,12 @@ def sandboxed let(:expect_methods) { [:receive, :allow, :expect_any_instance_of, :allow_any_instance_of] } it 'defaults to enabling both the :should and :expect syntaxes' do + # This is kinda a hack, but since we want to enforce use of + # the expect syntax within our specs here, we have modified the + # config setting, which makes it hard to get at the original + # default value. in spec_helper.rb we store the default value + # in $default_rspec_mocks_syntax so we can use it here. + RSpec::Mocks.configuration.syntax = $default_rspec_mocks_syntax expect(dbl).to respond_to(*should_methods) expect(self).to respond_to(*expect_methods) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e5f11ffcd..bcf5b4a7a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -58,11 +58,9 @@ def prevents(&block) expectations.syntax = :expect end - # TODO: switch most things to the `expect` syntax (and configure only that here) - # but keep a small number of specs that use the old syntax in order to test it - # (using the "with syntax" shared context defined below). config.mock_with :rspec do |mocks| - mocks.syntax = [:should, :expect] + $default_rspec_mocks_syntax = mocks.syntax + mocks.syntax = :expect end old_verbose = nil From 3deb657e8ee0c8554d0c097735405c1b0be48415 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 27 Feb 2014 00:05:38 -0800 Subject: [PATCH 0526/1179] Remove old comment. --- .../mocks/partial_double_using_mocks_directly_spec.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb index b6709e23a..8b7eac593 100644 --- a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb +++ b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb @@ -25,16 +25,6 @@ def existing_method let(:obj) { klass.new } - # See http://rubyforge.org/tracker/index.php?func=detail&aid=10263&group_id=797&atid=3149 - # specify "should clear expectations on verify" do - # obj.should_receive(:msg) - # obj.msg - # verify obj - # expect { - # obj.msg - # }.to raise_error(NoMethodError) - # - # end it "fails when expected message is not received" do expect(obj).to receive(:msg) expect { From 61ad84ddf861dfe730dbb02122d036fbf04dec23 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 27 Feb 2014 00:12:40 -0800 Subject: [PATCH 0527/1179] Update doc strings so they don't mention legacy APIs. --- spec/rspec/mocks/block_return_value_spec.rb | 4 ++-- spec/rspec/mocks/double_spec.rb | 14 +++++++------- spec/rspec/mocks/partial_double_spec.rb | 16 ++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 9315ee35a..2b5a481ed 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -1,5 +1,5 @@ describe "a double declaration with a block handed to:" do - describe "should_receive" do + describe "expect(...).to receive" do it "returns the value of executing the block" do obj = Object.new expect(obj).to receive(:foo) { 'bar' } @@ -15,7 +15,7 @@ end end - describe "stub" do + describe "allow(...).to receive" do it "returns the value of executing the block" do obj = Object.new allow(obj).to receive(:foo) { 'bar' } diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 246319c3b..3df39cd85 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -119,7 +119,7 @@ module Mocks end end - it "reports line number of expectation of unreceived message after #should_receive after similar stub" do + it "reports line number of expectation of unreceived message after a message expecation after similar stub" do allow(@double).to receive(:wont_happen) expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) begin @@ -197,7 +197,7 @@ module Mocks end context "when specifying a message should not be received with specific args" do - context "using `should_not_receive`" do + context "using `expect(...).not_to receive()`" do it 'passes when receiving the message with different args' do expect(@double).not_to receive(:not_expected).with("unexpected text") @double.not_expected "really unexpected text" @@ -205,7 +205,7 @@ module Mocks end end - context "using `should_receive().never`" do + context "using `expect(...).to receive().never`" do it 'passes when receiving the message with different args' do expect(@double).to receive(:not_expected).with("unexpected text").never @double.not_expected "really unexpected text" @@ -214,7 +214,7 @@ module Mocks end end - it 'does not get confused when `should_not_received` is used with a string and symbol message' do + it 'does not get confused when a negative expecation is used with a string and symbol message' do allow(@double).to receive(:foo) { 3 } expect(@double).not_to receive(:foo).with(1) expect(@double).not_to receive("foo").with(2) @@ -223,7 +223,7 @@ module Mocks verify @double end - it 'does not get confused when `should_received` is used with a string and symbol message' do + it 'does not get confused when a positive expectation is used with a string and symbol message' do expect(@double).to receive(:foo).with(1) expect(@double).to receive("foo").with(2) @@ -710,7 +710,7 @@ def add_call @calls = @calls + 1 end - it "calls the block after #should_receive" do + it "supports a block passed to `receive` for `expect`" do expect(@double).to receive(:foo) { add_call } @double.foo @@ -718,7 +718,7 @@ def add_call expect(@calls).to eq 1 end - it "calls the block after #should_receive after a similar stub" do + it "supports a block passed to `receive` for `expect` after a similar stub" do allow(@double).to receive(:foo).and_return(:bar) expect(@double).to receive(:foo) { add_call } diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index e378076a3..677ab055d 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -23,7 +23,7 @@ module Mocks object.blah end - it "should_not_receive mocks out the method" do + it "can disallow messages from being received" do expect(object).not_to receive(:fuhbar) expect { object.fuhbar @@ -33,40 +33,40 @@ module Mocks ) end - it "should_receive mocks out the method" do + it "can expect a message and set a return value" do expect(object).to receive(:foobar).with(:test_param).and_return(1) expect(object.foobar(:test_param)).to equal(1) end - it "should_receive handles a hash" do + it "can accept a hash as a message argument" do expect(object).to receive(:foobar).with(:key => "value").and_return(1) expect(object.foobar(:key => "value")).to equal(1) end - it "should_receive handles an inner hash" do + it "can accept an inner hash as a message argument" do hash = {:a => {:key => "value"}} expect(object).to receive(:foobar).with(:key => "value").and_return(1) expect(object.foobar(hash[:a])).to equal(1) end - it "should_receive returns a positive message expectation" do + it "can create a positive message expectation" do expect(expect(object).to receive(:foobar)).not_to be_negative object.foobar end - it "should_receive verifies method was called" do + it "verifies the method was called when expecting a message" do expect(object).to receive(:foobar).with(:test_param).and_return(1) expect { verify object }.to raise_error(RSpec::Mocks::MockExpectationError) end - it "should_receive also takes a String argument" do + it "can accept the string form of a message for a positive message expectation" do expect(object).to receive('foobar') object.foobar end - it "should_not_receive also takes a String argument" do + it "can accept the string form of a message for a negative message expectation" do expect(object).not_to receive('foobar') expect { object.foobar From 126e6f146fe33ad2449694fd926ad863e8004b6d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 27 Feb 2014 00:21:18 -0800 Subject: [PATCH 0528/1179] Add some basic sanity check specs for `should` syntax. Since we've migrated to the expect syntax we lost a lot of test coverage for the should syntax. For the most part we don't need everything covered for both because it's the same underlying mechanisms but some sanity tests are nice. --- spec/rspec/mocks/should_syntax_spec.rb | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index a6ae70c24..ed9f6e165 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -37,6 +37,19 @@ expect(double.stub(:foo).and_throw(:foo)).to be_nil end + it 'sets up a canned response' do + dbl = double + dbl.stub(:foo).and_return(3) + expect(dbl.foo).to eq(3) + end + + it 'can stub multiple messages using a hash' do + dbl = double + dbl.stub(:foo => 2, :bar => 1) + expect(dbl.foo).to eq(2) + expect(dbl.bar).to eq(1) + end + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.stub(:foo) @@ -45,6 +58,15 @@ def use_rspec_mocks end describe "#stub_chain" do + it 'can stub a sequence of messages' do + dbl = double + dbl.stub_chain(:foo, :bar, :baz => 17) + expect(dbl.foo.bar.baz).to eq(17) + expect { + dbl.foo.baz.bar + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.stub_chain(:foo, :bar) @@ -108,6 +130,25 @@ def obj.foo; :original; end end describe "#should_receive" do + it 'fails on verification if the message is not received' do + dbl = double + dbl.should_receive(:foo) + expect { verify_all }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it 'does not fail on verification if the message is received' do + dbl = double + dbl.should_receive(:foo) + dbl.foo + expect { verify_all }.not_to raise_error + end + + it 'can set a canned response' do + dbl = double + dbl.should_receive(:bar).and_return(3) + expect(dbl.bar).to eq(3) + end + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.should_receive(:foo) @@ -150,6 +191,18 @@ def use_rspec_mocks expect(Object.new.should_not_receive(:foobar)).to be_negative end + it 'fails when the message is received' do + dbl = double + dbl.should_not_receive(:foo) + expect { dbl.foo }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it 'does not fail on verification if the message is not received' do + dbl = double + dbl.should_not_receive(:foo) + expect { verify_all }.not_to raise_error + end + include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.should_not_receive(:foo) @@ -180,6 +233,17 @@ def use_rspec_mocks }.to change { RSpec::Mocks.space.any_instance_recorders.size }.by(1) end + it 'can stub a method' do + klass.any_instance.stub(:foo).and_return(2) + expect(klass.new.foo).to eq(2) + end + + it 'can mock a method' do + klass.any_instance.should_receive(:foo) + klass.new + expect { verify_all }.to raise_error(RSpec::Mocks::MockExpectationError) + end + context "invocation order" do describe "#stub" do it "raises an error if 'stub' follows 'with'" do From 8d883e086eb3eed9f6bf7f8df2e3ae5edf6ea5c9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 27 Feb 2014 08:00:27 -0800 Subject: [PATCH 0529/1179] Remove extra blank line. [ci skip] --- spec/rspec/mocks/configuration_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 6a69c185f..53772a5cb 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -127,7 +127,6 @@ def sandboxed end end - describe "configuring rspec-mocks directly" do it_behaves_like "configuring the syntax" do def configure_syntax(syntax) From 0cf08e063bd22a2f99f62abc131e7e4990e34296 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 1 Mar 2014 09:56:38 -0800 Subject: [PATCH 0530/1179] Remove newline from unimplemented error message. In rspec-fire it was possible for this error to contain multiple method names, so newlines made sense. Given how it is now implemented, this cannot happen. --- lib/rspec/mocks/error_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 3fcc9141d..d84289081 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -69,7 +69,7 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc # @private def raise_unimplemented_error(doubled_module, method_name) - __raise "%s does not implement:\n %s" % [ + __raise "%s does not implement: %s" % [ doubled_module.description, method_name ] From 2891fbb7121908bbf98422f5bb39a05a6afd16d3 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 1 Mar 2014 10:30:55 -0800 Subject: [PATCH 0531/1179] Include private methods when checking for implementation in verifying partial doubles. Fixes #603. --- Changelog.md | 1 + lib/rspec/mocks/verifying_proxy.rb | 2 +- spec/rspec/mocks/partial_double_spec.rb | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 885187513..02c8d39f6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Bug Fixes: * Fix bug in `expect(dbl).to receive_message_chain(:foo, :bar)` where it was not setting an expectation for the last message in the chain. (Jonathan del Strother) +* Allow verifying partial doubles to have private methods stubbed. (Xavier Shay) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index fedb513bf..6a4bfa5e1 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -145,7 +145,7 @@ class VerifyingExistingMethodDouble < VerifyingMethodDouble def initialize(object, method_name, proxy) super(object, method_name, proxy, self) - @valid_method = object.respond_to?(method_name) + @valid_method = object.respond_to?(method_name, true) # Trigger an eager find of the original method since if we find it any # later we end up getting a stubbed method with incorrect arity. diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 677ab055d..5f2b24ed7 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -245,6 +245,12 @@ def method_missing(method_name, *args) super end end + + private + + def defined_private_method + "works" + end end end @@ -259,6 +265,11 @@ def method_missing(method_name, *args) expect(object.implemented).to eq("works") end + it 'allows private methods to be expected' do + expect(object).to receive(:defined_private_method).and_call_original + expect(object.send(:defined_private_method)).to eq("works") + end + it 'does not allow a non-existing method to be expected' do prevents { expect(object).to receive(:unimplemented) } end From 160da9076d0f035cf1c3f06dc756180e3d9888fd Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 1 Mar 2014 15:14:03 -0800 Subject: [PATCH 0532/1179] Add most missing documentation. --- lib/rspec/mocks.rb | 5 +++++ lib/rspec/mocks/any_instance/recorder.rb | 5 +++++ lib/rspec/mocks/configuration.rb | 2 ++ lib/rspec/mocks/error_generator.rb | 2 +- lib/rspec/mocks/extensions/marshal.rb | 1 + lib/rspec/mocks/message_expectation.rb | 2 ++ lib/rspec/mocks/mutate_const.rb | 1 + lib/rspec/mocks/space.rb | 1 + lib/rspec/mocks/targets.rb | 5 +++++ lib/rspec/mocks/test_double.rb | 3 +++ lib/rspec/mocks/version.rb | 2 ++ 11 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 4e613a5d1..48bb30e54 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -25,6 +25,8 @@ version ].each { |name| RSpec::Support.require_rspec_mocks name } +# Share the top-level RSpec namespace, because we are a core supported +# extension. module RSpec # Contains top-level utility methods. While this contains a few # public methods, these are not generally meant to be called from @@ -89,6 +91,8 @@ def self.expect_message(subject, message, opts={}, &block) space.proxy_for(subject).add_message_expectation(orig_caller, message, opts, &block) end + # Call the passed block and verify mocks after it has executed. This allows + # mock usage in arbitrary places, such as a `before(:all)` hook. def self.with_temporary_scope setup @@ -113,6 +117,7 @@ class << self; attr_reader :space; end autoload :ExpectChain, "rspec/mocks/message_chain" autoload :StubChain, "rspec/mocks/message_chain" + # Namespace for mock-related matchers. module Matchers autoload :HaveReceived, "rspec/mocks/matchers/have_received" autoload :Receive, "rspec/mocks/matchers/receive" diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 2ba827ce2..dcb45a268 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -69,6 +69,9 @@ def should_receive(method_name, &block) message_chains.add(method_name, PositiveExpectationChain.new(self, method_name, &block)) end + # The opposite of `should_receive` + # + # @see Methods#should_not_receive def should_not_receive(method_name, &block) should_receive(method_name, &block).never end @@ -117,10 +120,12 @@ def instance_that_received(method_name) @played_methods[method_name] end + # @private def build_alias_method_name(method_name) "__#{method_name}_without_any_instance__" end + # @private def already_observing?(method_name) @observed_methods.include?(method_name) || super_class_observing?(method_name) end diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index ef604d8b3..eb7c102af 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -126,6 +126,8 @@ def reset_syntaxes_to_default end end + # Mocks specific configuration, as distinct from `RSpec.configuration` + # which is core RSpec configuration. def self.configuration @configuration ||= Configuration.new end diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index d84289081..72caf55af 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -17,7 +17,7 @@ module Mocks UnsupportedMatcherError = Class.new(StandardError) # @private NegationUnsupportedError = Class.new(StandardError) - + # @private VerifyingDoubleNotDefinedError = Class.new(StandardError) # @private diff --git a/lib/rspec/mocks/extensions/marshal.rb b/lib/rspec/mocks/extensions/marshal.rb index 47b8fbfea..3224ea061 100644 --- a/lib/rspec/mocks/extensions/marshal.rb +++ b/lib/rspec/mocks/extensions/marshal.rb @@ -1,3 +1,4 @@ +# Re-opening core class to monkey-patch it. Need to make this opt-in. module Marshal class << self # Duplicates any mock objects before serialization. Otherwise, diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index ad5b9ffa2..baaf649f8 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -5,6 +5,8 @@ module Mocks # for a message. While this same effect can be achieved using a standard # MessageExpecation, this version is much faster and so can be used as an # optimization. + # + # @private class SimpleMessageExpectation def initialize(message, response, error_generator, backtrace_line = nil) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index 56226dc94..e890490a4 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -123,6 +123,7 @@ def hidden? @hidden end + # The default `to_s` isn't very useful, so a custom version is provided. def to_s "#<#{self.class.name} #{name}>" end diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 1efd44d72..b407ab0d8 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -137,6 +137,7 @@ def id_for(object) end end + # @private class NestedSpace < Space def initialize(parent) @parent = parent diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 19aa143c6..557977228 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -1,5 +1,6 @@ module RSpec module Mocks + # @private class TargetBase def initialize(target) @target = target @@ -61,6 +62,7 @@ def expression end end + # @private class AllowanceTarget < TargetBase EXPRESSION = :allow delegate_to :setup_allowance @@ -68,6 +70,7 @@ class AllowanceTarget < TargetBase disallow_negation :to_not end + # @private class ExpectationTarget < TargetBase EXPRESSION = :expect delegate_to :setup_expectation @@ -75,6 +78,7 @@ class ExpectationTarget < TargetBase delegate_not_to :setup_negative_expectation, :from => :to_not end + # @private class AnyInstanceAllowanceTarget < TargetBase EXPRESSION = :allow_any_instance_of delegate_to :setup_any_instance_allowance @@ -82,6 +86,7 @@ class AnyInstanceAllowanceTarget < TargetBase disallow_negation :to_not end + # @private class AnyInstanceExpectationTarget < TargetBase EXPRESSION = :expect_any_instance_of delegate_to :setup_any_instance_expectation diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index af067e546..f59f8e385 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -57,10 +57,13 @@ def __build_mock_proxy(order_group) __raise_expired_error or TestDoubleProxy.new(self, order_group, @name) end + # @private def __disallow_further_usage! @__expired = true end + # Override for default freeze implementation to prevent freezing of test + # doubles. def freeze RSpec.warn_with("WARNING: you attempted to freeze a test double. This is explicitly a no-op as freezing doubles can lead to undesired behaviour when resetting tests.") end diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 4d4da963d..d3099aeb7 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -1,6 +1,8 @@ module RSpec module Mocks + # Version information for RSpec mocks. module Version + # Version of RSpec mocks currently in use in SemVer format. STRING = '3.0.0.beta2' end end From 22850ac78ad29a5afca77f5f6f21d431e898a3e4 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 1 Mar 2014 15:27:55 -0800 Subject: [PATCH 0533/1179] Replace many instances of @api private with @private so yard ignores it properly. --- .../mocks/any_instance/expectation_chain.rb | 4 ++-- lib/rspec/mocks/any_instance/recorder.rb | 2 +- lib/rspec/mocks/argument_matchers.rb | 22 +++++++++---------- lib/rspec/mocks/example_methods.rb | 6 ++--- lib/rspec/mocks/message_expectation.rb | 3 ++- lib/rspec/mocks/method_signature_verifier.rb | 7 ++---- lib/rspec/mocks/mutate_const.rb | 15 +++++++------ lib/rspec/mocks/object_reference.rb | 11 ++++++---- lib/rspec/mocks/space.rb | 2 +- lib/rspec/mocks/syntax.rb | 4 ++-- lib/rspec/mocks/verifying_double.rb | 14 +++++++----- lib/rspec/mocks/verifying_proxy.rb | 10 ++++----- 12 files changed, 53 insertions(+), 47 deletions(-) diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 5c4e848b1..ff85dfc98 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module AnyInstance - # @api private + # @private class ExpectationChain < Chain def expectation_fulfilled? @expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times) @@ -18,7 +18,7 @@ def verify_invocation_order(rspec_method_name, *args, &block) end end - # @api private + # @private class PositiveExpectationChain < ExpectationChain private diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index dcb45a268..e794f8168 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -49,7 +49,7 @@ def stub_chain(*method_names_and_optional_return_values, &block) end end - # @api private + # @private def expect_chain(*method_names_and_optional_return_values, &block) @expectation_set = true normalize_chain(*method_names_and_optional_return_values) do |method_name, args| diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 95006afa9..d81644b14 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -115,42 +115,42 @@ def kind_of(klass) alias_method :a_kind_of, :kind_of - # @api private + # @private def self.anythingize_lonely_keys(*args) hash = args.last.class == Hash ? args.delete_at(-1) : {} args.each { | arg | hash[arg] = AnyArgMatcher.new } hash end - # @api private + # @private class AnyArgsMatcher def description "any args" end end - # @api private + # @private class AnyArgMatcher def ===(other) true end end - # @api private + # @private class NoArgsMatcher def description "no args" end end - # @api private + # @private class BooleanMatcher def ===(value) true == value || false == value end end - # @api private + # @private class BaseHashMatcher def initialize(expected) @expected = expected @@ -169,7 +169,7 @@ def description(name) end end - # @api private + # @private class HashIncludingMatcher < BaseHashMatcher def ===(actual) super(:all?, actual) @@ -180,7 +180,7 @@ def description end end - # @api private + # @private class HashExcludingMatcher < BaseHashMatcher def ===(actual) super(:none?, actual) @@ -191,7 +191,7 @@ def description end end - # @api private + # @private class ArrayIncludingMatcher def initialize(expected) @expected = expected @@ -206,7 +206,7 @@ def description end end - # @api private + # @private class DuckTypeMatcher def initialize(*methods_to_respond_to) @methods_to_respond_to = methods_to_respond_to @@ -217,7 +217,7 @@ def ===(value) end end - # @api private + # @private class InstanceOf def initialize(klass) @klass = klass diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 799c11c3e..00a2e44c0 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -269,7 +269,7 @@ def have_received(method_name, &block) # # @note If you disable the `:expect` syntax this method will be undefined. - # @api private + # @private def self.included(klass) klass.class_exec do # This gets mixed in so that if `RSpec::Matchers` is included in @@ -278,7 +278,7 @@ def self.included(klass) end end - # @api private + # @private def self.declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? @@ -292,7 +292,7 @@ def self.declare_verifying_double(type, ref, *args) declare_double(type, ref, *args) end - # @api private + # @private def self.declare_double(type, *args) args << {} unless Hash === args.last type.new(*args) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index baaf649f8..edb1b6b70 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -635,7 +635,8 @@ def cannot_modify_further_error end # Insert original locations into stacktraces - # @api private + # + # @private class InsertOntoBacktrace def self.line(location) yield diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 8edd378e4..6a480e834 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -5,7 +5,7 @@ module Mocks # Extracts info about the number of arguments and allowed/required # keyword args of a given method. # - # @api private + # @private class MethodSignature attr_reader :min_non_kw_args, :max_non_kw_args @@ -143,9 +143,8 @@ def classify_parameters # Figures out wheter a given method can accept various arguments. # Surprisingly non-trivial. # - # @api private + # @private class MethodSignatureVerifier - # @api private attr_reader :non_kw_args, :kw_args def initialize(signature, args) @@ -153,14 +152,12 @@ def initialize(signature, args) @non_kw_args, @kw_args = split_args(*args) end - # @api private def valid? missing_kw_args.empty? && invalid_kw_args.empty? && valid_non_kw_args? end - # @api private def error_message if missing_kw_args.any? "Missing required keyword arguments: %s" % [ diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index e890490a4..1bed7b156 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -2,7 +2,8 @@ module RSpec module Mocks # Provides recursive constant lookup methods useful for # constant stubbing. - # @api private + # + # @private module RecursiveConstMethods # We only want to consider constants that are defined directly on a # particular module, and not include top-level/inherited constants. @@ -129,7 +130,7 @@ def to_s end alias inspect to_s - # @api private + # @private def self.unmutated(name) const = new(name) const.previously_defined = recursive_const_defined?(name) @@ -196,7 +197,7 @@ def self.hide(constant_name) # Contains common functionality used by all of the constant mutators. # - # @api private + # @private class BaseMutator include RecursiveConstMethods @@ -226,7 +227,7 @@ def idempotently_reset # Hides a defined constant for the duration of an example. # - # @api private + # @private class ConstantHider < BaseMutator def mutate return unless @defined = recursive_const_defined?(full_constant_name) @@ -254,7 +255,7 @@ def reset # Replaces a defined constant for the duration of an example. # - # @api private + # @private class DefinedConstantReplacer < BaseMutator def mutate @context = recursive_const_get(@context_parts.join('::')) @@ -320,7 +321,7 @@ def verify_constants_to_transfer! # Sets an undefined constant for the duration of an example. # - # @api private + # @private class UndefinedConstantSetter < BaseMutator def mutate @parent = @context_parts.inject(Object) do |klass, name| @@ -362,7 +363,7 @@ def name_for(parent, name) # the mutator is correctly registered so it can be backed out at the end # of the test. # - # @api private + # @private def self.mutate(mutator) ::RSpec::Mocks.space.register_constant_mutator(mutator) mutator.mutate diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index f99f5f7e7..9ccda42bc 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -1,7 +1,7 @@ module RSpec module Mocks - # @api private + # @private class ObjectReference # Returns an appropriate Object or Module reference based # on the given argument. @@ -22,7 +22,8 @@ def self.for(object_module_or_name, allow_direct_object_refs = false) # Used when an object is passed to `object_double`. # Represents a reference to that object. - # @api private + # + # @private class DirectObjectReference def initialize(object) @object = object @@ -48,7 +49,8 @@ def when_loaded # Used when a module is passed to `class_double` or `instance_double`. # Represents a reference to that module. - # @api private + # + # @private class DirectModuleReference < DirectObjectReference def const_to_replace @object.name @@ -60,7 +62,8 @@ def const_to_replace # or `object_double`. # Represents a reference to the object named (via a constant lookup) # by the string. - # @api private + # + # @private class NamedObjectReference def initialize(const_name) @const_name = const_name diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index b407ab0d8..bf69c4520 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -39,7 +39,7 @@ def raise_lifecycle_message end end - # @api private + # @private class Space attr_reader :proxies, :any_instance_recorders, :expectation_ordering diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 12bcb4e0d..5c6bc520c 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -4,12 +4,12 @@ module Mocks # Provides methods for enabling and disabling the available syntaxes # provided by rspec-mocks. module Syntax - # @api private + # @private def self.warn_about_should! @warn_about_should = true end - # @api private + # @private def self.warn_unless_should_configured(method_name ,replacement = "the new `:expect` syntax or explicitly enable `:should`") if @warn_about_should RSpec.deprecate( diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index a0ab257ec..ee35a24a8 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -4,7 +4,7 @@ module RSpec module Mocks - # @api private + # @private module VerifyingDouble def respond_to?(message, include_private=false) return super unless null_object? @@ -57,7 +57,8 @@ def initialize(*args) # A mock providing a custom proxy that can verify the validity of any # method stubs or expectations against the public instance methods of the # given class. - # @api private + # + # @private class InstanceVerifyingDouble include TestDouble include VerifyingDouble @@ -81,7 +82,8 @@ def __build_mock_proxy(order_group) # An awkward module necessary because we cannot otherwise have # ClassVerifyingDouble inherit from Module and still share these methods. - # @api private + # + # @private module ObjectVerifyingDoubleMethods include TestDouble include VerifyingDouble @@ -106,7 +108,8 @@ def as_stubbed_const(options = {}) # Similar to an InstanceVerifyingDouble, except that it verifies against # public methods of the given object. - # @api private + # + # @private class ObjectVerifyingDouble include ObjectVerifyingDoubleMethods end @@ -114,7 +117,8 @@ class ObjectVerifyingDouble # Effectively the same as an ObjectVerifyingDouble (since a class is a type # of object), except with Module in the inheritance chain so that # transferring nested constants to work. - # @api private + # + # @private class ClassVerifyingDouble < Module include ObjectVerifyingDoubleMethods end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 6a4bfa5e1..b779ea7d1 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -4,7 +4,7 @@ module RSpec module Mocks - # @api private + # @private module VerifyingProxyMethods def add_stub(location, method_name, opts={}, &implementation) ensure_implemented(method_name) @@ -53,7 +53,7 @@ def ensure_publicly_implemented(method_name, object) # loaded, otherwise they are disabled. This allows for testing in # isolation. # - # @api private + # @private class VerifyingProxy < Proxy include VerifyingProxyMethods @@ -82,7 +82,7 @@ def visibility_for(method_name) end end - # @api private + # @private class VerifyingPartialDoubleProxy < PartialDoubleProxy include VerifyingProxyMethods @@ -102,7 +102,7 @@ def method_reference end end - # @api private + # @private class VerifyingMethodDouble < MethodDouble def initialize(object, method_name, proxy, method_reference) super(object, method_name, proxy) @@ -140,7 +140,7 @@ def validate_arguments!(actual_args) # collapse the reference and the method double into a single object so that # we can access the original pristine method definition. # - # @api private + # @private class VerifyingExistingMethodDouble < VerifyingMethodDouble def initialize(object, method_name, proxy) super(object, method_name, proxy, self) From 2992a227f240ef9df485ed036b3bd9c5b624ce58 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 2 Mar 2014 08:42:26 -0800 Subject: [PATCH 0534/1179] Remove invalid @public tag. --- lib/rspec/mocks/error_generator.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 72caf55af..d067fd8fe 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -1,15 +1,12 @@ module RSpec module Mocks - # @public # Raised when a message expectation is not satisfied. MockExpectationError = Class.new(Exception) - # @public # Raised when a test double is used after it has been torn # down (typically at the end of an rspec-core example). ExpiredTestDoubleError = Class.new(MockExpectationError) - # @public # Raised when doubles or partial doubles are used outside of the per-test lifecycle. OutsideOfExampleError = Class.new(StandardError) From 79cba2efc0913ced4fda944cb747ad7b402590c8 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 2 Mar 2014 08:47:29 -0800 Subject: [PATCH 0535/1179] Fix up some warnings. --- lib/rspec/mocks/argument_list_matcher.rb | 5 ++--- lib/rspec/mocks/verifying_message_expecation.rb | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index f65af7af6..927e36fec 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -35,8 +35,7 @@ class ArgumentListMatcher attr_reader :expected_args # @api public - # @param [Array] *expected_args a list of expected literals and/or argument matchers - # @param [Block] block a block with arity matching the expected + # @param [Array] expected_args a list of expected literals and/or argument matchers # # Initializes an `ArgumentListMatcher` with a collection of literal # values and/or argument matchers. @@ -54,7 +53,7 @@ def initialize(*expected_args) end # @api public - # @param [Array] *args + # @param [Array] args # # Matches each element in the `expected_args` against the element in the same # position of the arguments passed to `new`. diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 99279d633..7185f536d 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -23,7 +23,6 @@ def initialize(*args) super end - # @override def with(*args, &block) unless ArgumentMatchers::AnyArgsMatcher === args.first expected_args = if ArgumentMatchers::NoArgsMatcher === args.first From ee143028b45a710f9c7c9ab032608c8294fe4f24 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 3 Mar 2014 04:23:04 +1100 Subject: [PATCH 0536/1179] Add missing descriptions to argument_matchers and modernise their specs --- lib/rspec/mocks/argument_matchers.rb | 18 +- spec/rspec/mocks/argument_matchers_spec.rb | 323 ++++++++++++++++++ .../mocks/array_including_matcher_spec.rb | 2 +- .../mocks/failing_argument_matchers_spec.rb | 182 ---------- .../mocks/passing_argument_matchers_spec.rb | 143 -------- spec/spec_helper.rb | 1 + spec/support/matchers.rb | 15 + 7 files changed, 357 insertions(+), 327 deletions(-) create mode 100644 spec/rspec/mocks/argument_matchers_spec.rb delete mode 100644 spec/rspec/mocks/failing_argument_matchers_spec.rb delete mode 100644 spec/rspec/mocks/passing_argument_matchers_spec.rb create mode 100644 spec/support/matchers.rb diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 95006afa9..e187f520c 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -134,6 +134,10 @@ class AnyArgMatcher def ===(other) true end + + def description + "anything" + end end # @api private @@ -148,6 +152,10 @@ class BooleanMatcher def ===(value) true == value || false == value end + + def description + "boolean" + end end # @api private @@ -202,7 +210,7 @@ def ===(actual) end def description - "array_including(#{@expected.join(",")})" + "array_including(#{@expected.join(", ")})" end end @@ -215,6 +223,10 @@ def initialize(*methods_to_respond_to) def ===(value) @methods_to_respond_to.all? {|message| value.respond_to?(message)} end + + def description + "duck_type(#{@methods_to_respond_to.map(&:inspect).join(', ')})" + end end # @api private @@ -226,6 +238,10 @@ def initialize(klass) def ===(actual) actual.instance_of?(@klass) end + + def description + "an_instance_of(#{@klass.name})" + end end end diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb new file mode 100644 index 000000000..8cb68c23b --- /dev/null +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -0,0 +1,323 @@ +module RSpec + module Mocks + describe "argument matchers matching" do + let(:a_double) { double } + + after(:each, :reset => true) do + reset a_double + end + + describe "boolean" do + it "accepts true as boolean" do + expect(a_double).to receive(:random_call).with(boolean) + a_double.random_call(true) + end + + it "accepts false as boolean" do + expect(a_double).to receive(:random_call).with(boolean) + a_double.random_call(false) + end + + it "rejects non boolean", :reset => true do + expect(a_double).to receive(:random_call).with(boolean) + expect { + a_double.random_call("false") + }.to fail_matching "expected: (boolean)" + end + end + + describe "kind_of" do + it "accepts fixnum as kind_of(Numeric)" do + expect(a_double).to receive(:random_call).with(kind_of(Numeric)) + a_double.random_call(1) + end + + it "accepts float as kind_of(Numeric)" do + expect(a_double).to receive(:random_call).with(kind_of(Numeric)) + a_double.random_call(1.5) + end + + it "handles non matching kinds nicely", :reset => true do + expect(a_double).to receive(:random_call).with(kind_of(Numeric)) + expect { + a_double.random_call(true) + }.to fail_matching "expected: (Numeric)" + end + end + + describe "instance_of" do + it "accepts fixnum as instance_of(Fixnum)" do + expect(a_double).to receive(:random_call).with(instance_of(Fixnum)) + a_double.random_call(1) + end + + it "does NOT accept fixnum as instance_of(Numeric)" do + expect(a_double).not_to receive(:random_call).with(instance_of(Numeric)) + a_double.random_call(1) + end + + it "does NOT accept float as instance_of(Numeric)" do + expect(a_double).not_to receive(:random_call).with(instance_of(Numeric)) + a_double.random_call(1.5) + end + + it "rejects non numeric", :reset => true do + expect(a_double).to receive(:random_call).with(an_instance_of(Numeric)) + expect { a_double.random_call("1") }.to fail + end + + it "rejects non string", :reset => true do + expect(a_double).to receive(:random_call).with(an_instance_of(String)) + expect { a_double.random_call(123) }.to fail + end + + it "handles non matching instances nicely", :reset => true do + expect(a_double).to receive(:random_call).with(instance_of(Numeric)) + expect { + a_double.random_call(1.5) + }.to fail_matching "expected: (an_instance_of(Numeric))" + end + end + + describe "anything" do + it "accepts string as anything" do + expect(a_double).to receive(:random_call).with("a", anything, "c") + a_double.random_call("a", "whatever", "c") + end + + it "doesn't accept no arguments" do + expect(a_double).to_not receive(:random_call).with(anything) + a_double.random_call + end + + it "handles non matching instances nicely", :reset => true do + expect(a_double).to receive(:random_call).with(anything) + expect { a_double.random_call }.to fail_matching "expected: (anything)" + end + end + + describe "duck_type" do + it "matches duck type with one method" do + expect(a_double).to receive(:random_call).with(duck_type(:length)) + a_double.random_call([]) + end + + it "matches duck type with two methods" do + expect(a_double).to receive(:random_call).with(duck_type(:abs, :div)) + a_double.random_call(1) + end + + it "rejects goose when expecting a duck", :reset => true do + expect(a_double).to receive(:random_call).with(duck_type(:abs, :div)) + expect { + a_double.random_call("I don't respond to :abs or :div") + }.to fail_matching "expected: (duck_type(:abs, :div))" + end + end + + describe "any_args" do + it "matches no args against any_args" do + expect(a_double).to receive(:random_call).with(any_args) + a_double.random_call + end + + it "matches one arg against any_args" do + expect(a_double).to receive(:random_call).with(any_args) + a_double.random_call("a string") + end + + it "handles non matching instances nicely", :reset => true do + expect(a_double).to receive(:random_call).with(1, any_args) + expect { a_double.random_call }.to fail_matching "expected: (1, any args)" + end + end + + describe "no_args" do + it "matches no args against no_args" do + expect(a_double).to receive(:random_call).with(no_args) + a_double.random_call + end + + it "fails no_args with one arg", :reset => true do + expect(a_double).to receive(:msg).with(no_args) + expect { a_double.msg(37) }.to fail_matching "expected: (no args)" + end + end + + describe "hash_including" do + it "matches hash with hash_including same hash" do + expect(a_double).to receive(:random_call).with(hash_including(:a => 1)) + a_double.random_call(:a => 1) + end + + it "fails hash_including with missing key", :reset => true do + expect(a_double).to receive(:random_call).with(hash_including(:a => 1)) + expect { + a_double.random_call(:a => 2) + }.to fail_matching "expected: (hash_including(:a=>1))" + end + end + + describe "hash_excluding" do + it "matches hash with hash_excluding same hash" do + expect(a_double).to receive(:random_call).with(hash_excluding(:a => 1)) + a_double.random_call(:a => 2) + end + + it "handles non matching instances nicely", :reset => true do + expect(a_double).to receive(:random_call).with(hash_excluding(:a => 1)) + expect { + a_double.random_call(:a => 1) + }.to fail_matching "expected: (hash_not_including(:a=>1))" + end + end + + describe "array_including" do + it "matches array with array_including same array" do + expect(a_double).to receive(:random_call).with(array_including(1, 2)) + a_double.random_call([1, 2]) + end + + it "fails array_including when args aren't array", :reset => true do + expect(a_double).to receive(:msg).with(array_including(1, 2, 3)) + expect { + a_double.msg(1, 2, 3) + }.to fail_matching "expected: (array_including(1, 2, 3))" + end + + it "fails array_including when arg doesn't contain all elements", :reset => true do + expect(a_double).to receive(:msg).with(array_including(1, 2, 3)) + expect { + a_double.msg([1, 2]) + }.to fail_matching "expected: (array_including(1, 2, 3))" + end + end + + context "handling arbitary matchers" do + it "matches any arbitrary object using #===" do + matcher = double + expect(matcher).to receive(:===).with(4).and_return(true) + + expect(a_double).to receive(:foo).with(matcher) + a_double.foo(4) + end + + it "matches against a Matcher", :reset => true do + expect(a_double).to receive(:msg).with(equal(3)) + # This spec is generating warnings on 1.8.7, not sure why so + # this does with_isolated_stderr to kill them. @samphippen 3rd Jan 2013. + expect { with_isolated_stderr { a_double.msg(37) } }.to fail_matching "expected: (equal 3)" + end + + it "fails when given an arbitrary object that returns false from #===", :reset => true do + matcher = double + expect(matcher).to receive(:===).with(4).at_least(:once).and_return(false) + + expect(a_double).to receive(:foo).with(matcher) + + expect { a_double.foo(4) }.to fail + end + end + + context "handling non-matcher arguments" do + it "matches string against regexp" do + expect(a_double).to receive(:random_call).with(/bcd/) + a_double.random_call("abcde") + end + + it "matches regexp against regexp" do + expect(a_double).to receive(:random_call).with(/bcd/) + a_double.random_call(/bcd/) + end + + it "fails if regexp does not match submitted string", :reset => true do + expect(a_double).to receive(:random_call).with(/bcd/) + expect { a_double.random_call("abc") }.to fail + end + + it "fails if regexp does not match submitted regexp", :reset => true do + expect(a_double).to receive(:random_call).with(/bcd/) + expect { a_double.random_call(/bcde/) }.to fail + end + + it "matches against a hash submitted and received by value" do + expect(a_double).to receive(:random_call).with(:a => "a", :b => "b") + a_double.random_call(:a => "a", :b => "b") + end + + it "matches against a hash submitted by reference and received by value" do + opts = {:a => "a", :b => "b"} + expect(a_double).to receive(:random_call).with(opts) + a_double.random_call(:a => "a", :b => "b") + end + + it "matches against a hash submitted by value and received by reference" do + opts = {:a => "a", :b => "b"} + expect(a_double).to receive(:random_call).with(:a => "a", :b => "b") + a_double.random_call(opts) + end + + it "fails for a hash w/ wrong values", :reset => true do + expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") + expect do + a_double.random_call(:a => "b", :c => "e") + end.to fail_matching /expected: \({(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")}\)/ + end + + it "fails for a hash w/ wrong keys", :reset => true do + expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") + expect do + a_double.random_call("a" => "b", "c" => "d") + end.to fail_matching /expected: \({(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")}\)/ + end + + it "matches a class against itself" do + expect(a_double).to receive(:foo).with(Fixnum) + a_double.foo(Fixnum) + end + + it "fails a class against an unrelated class", :reset => true do + expect(a_double).to receive(:foo).with(Fixnum) + expect { a_double.foo(Hash) }.to fail + end + + it "matches a class against an instance of itself" do + expect(a_double).to receive(:foo).with(Fixnum) + a_double.foo(3) + end + + it "fails a class against an object of a different type", :reset => true do + expect(a_double).to receive(:foo).with(Fixnum) + expect { a_double.foo(3.2) }.to fail + end + + it "fails with zero arguments", :reset => true do + expect do + expect(a_double).to receive(:msg).with {|arg| expect(arg).to eq :received } + end.to raise_error(ArgumentError, /must have at least one argument/) + end + + it "fails with sensible message when args respond to #description", :reset => true do + arg = double(:description => nil, :inspect => "my_thing") + + expect(a_double).to receive(:msg).with(3) + expect { a_double.msg arg }.to fail_matching "got: (my_thing)" + end + + it "fails with sensible message when arg#description is nil", :reset => true do + arg = double(:description => nil, :inspect => "my_thing") + + expect(a_double).to receive(:msg).with(arg) + expect { a_double.msg 3 }.to fail_matching "expected: (my_thing)" + end + + it "fails with sensible message when arg#description is blank", :reset => true do + arg = double(:description => "", :inspect => "my_thing") + + expect(a_double).to receive(:msg).with(arg) + expect { a_double.msg 3 }.to fail_matching "expected: (my_thing)" + end + end + end + end +end diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb index 9d6521319..77c59f84d 100644 --- a/spec/rspec/mocks/array_including_matcher_spec.rb +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -3,7 +3,7 @@ module Mocks module ArgumentMatchers describe ArrayIncludingMatcher do it "describes itself properly" do - expect(ArrayIncludingMatcher.new([1, 2, 3]).description).to eq "array_including(1,2,3)" + expect(ArrayIncludingMatcher.new([1, 2, 3]).description).to eq "array_including(1, 2, 3)" end context "passing" do diff --git a/spec/rspec/mocks/failing_argument_matchers_spec.rb b/spec/rspec/mocks/failing_argument_matchers_spec.rb deleted file mode 100644 index 27058637a..000000000 --- a/spec/rspec/mocks/failing_argument_matchers_spec.rb +++ /dev/null @@ -1,182 +0,0 @@ -module RSpec - module Mocks - describe "failing MockArgumentMatchers" do - before(:each) do - @double = double("double") - @reporter = double("reporter").as_null_object - end - - after(:each) do - reset @double - end - - it "rejects non boolean" do - expect(@double).to receive(:random_call).with(boolean()) - expect do - @double.random_call("false") - end.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "rejects non numeric" do - expect(@double).to receive(:random_call).with(an_instance_of(Numeric)) - expect do - @double.random_call("1") - end.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "rejects non string" do - expect(@double).to receive(:random_call).with(an_instance_of(String)) - expect do - @double.random_call(123) - end.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "rejects goose when expecting a duck" do - expect(@double).to receive(:random_call).with(duck_type(:abs, :div)) - expect { @double.random_call("I don't respond to :abs or :div") }.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "fails if regexp does not match submitted string" do - expect(@double).to receive(:random_call).with(/bcd/) - expect { @double.random_call("abc") }.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "fails if regexp does not match submitted regexp" do - expect(@double).to receive(:random_call).with(/bcd/) - expect { @double.random_call(/bcde/) }.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "fails for a hash w/ wrong values" do - expect(@double).to receive(:random_call).with(:a => "b", :c => "d") - expect do - @double.random_call(:a => "b", :c => "e") - end.to raise_error(RSpec::Mocks::MockExpectationError, /Double "double" received :random_call with unexpected arguments\n expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)\n got: \(\{(:a=>\"b\", :c=>\"e\"|:c=>\"e\", :a=>\"b\")\}\)/) - end - - it "fails for a hash w/ wrong keys" do - expect(@double).to receive(:random_call).with(:a => "b", :c => "d") - expect do - @double.random_call("a" => "b", "c" => "d") - end.to raise_error(RSpec::Mocks::MockExpectationError, /Double "double" received :random_call with unexpected arguments\n expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)\n got: \(\{(\"a\"=>\"b\", \"c\"=>\"d\"|\"c\"=>\"d\", \"a\"=>\"b\")\}\)/) - end - - it "matches against a Matcher" do - # This spec is generating warnings on 1.8.7, not sure why so - # this does with_isolated_stderr to kill them. @samphippen 3rd Jan 2013. - expect do - expect(@double).to receive(:msg).with(equal(3)) - with_isolated_stderr { @double.msg(37) } - end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (equal 3)\n got: (37)") - end - - it "fails no_args with one arg" do - expect do - expect(@double).to receive(:msg).with(no_args) - @double.msg(37) - end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (no args)\n got: (37)") - end - - it "fails hash_including with missing key" do - expect do - expect(@double).to receive(:msg).with(hash_including(:a => 1)) - @double.msg({}) - end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (hash_including(:a=>1))\n got: ({})") - end - - it "fails array_including when args aren't array" do - expect do - expect(@double).to receive(:msg).with(array_including(1,2,3)) - @double.msg(1,2,3) - end.to raise_error(/array_including\(1,2,3\)/) - end - - it "fails array_including when arg doesn't contain all elements" do - expect do - expect(@double).to receive(:msg).with(array_including(1,2,3)) - @double.msg(1,2) - end.to raise_error(/array_including\(1,2,3\)/) - end - - it "fails with zero arguments" do - expect do - expect(@double).to receive(:msg).with {|arg| expect(arg).to eq :received } - end.to raise_error(ArgumentError, /must have at least one argument/) - end - - it "fails when given an arbitrary object that returns false from #===" do - matcher = double - expect(matcher).to receive(:===).with(4).at_least(:once).and_return(false) - - expect(@double).to receive(:foo).with(matcher) - - expect { - @double.foo(4) - }.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "fails with sensible message when args respond to #description" do - arg = Class.new do - def description - end - - def inspect - "my_thing" - end - end.new - - expect do - expect(@double).to receive(:msg).with(3) - @double.msg arg - end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (3)\n got: (my_thing)") - end - - it "fails with sensible message when arg#description is nil" do - arg = Class.new do - def description - end - - def inspect - "my_thing" - end - end.new - - expect do - expect(@double).to receive(:msg).with(arg) - @double.msg 3 - end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (my_thing)\n got: (3)") - end - - it "fails with sensible message when arg#description is blank" do - arg = Class.new do - def description - "" - end - - def inspect - "my_thing" - end - end.new - - expect do - expect(@double).to receive(:msg).with(arg) - @double.msg 3 - end.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"double\" received :msg with unexpected arguments\n expected: (my_thing)\n got: (3)") - end - - it "fails a class against an unrelated class" do - expect(@double).to receive(:foo).with(Fixnum) - expect { - @double.foo(Hash) - }.to raise_error(RSpec::Mocks::MockExpectationError) - end - - it "fails a class against an object of a different type" do - expect(@double).to receive(:foo).with(Fixnum) - - expect { - @double.foo(3.2) - }.to raise_error(RSpec::Mocks::MockExpectationError) - end - end - end -end diff --git a/spec/rspec/mocks/passing_argument_matchers_spec.rb b/spec/rspec/mocks/passing_argument_matchers_spec.rb deleted file mode 100644 index 6a3b33bc5..000000000 --- a/spec/rspec/mocks/passing_argument_matchers_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -module RSpec - module Mocks - describe "Passing argument matchers" do - before(:each) do - @double = double('double') - allow(Kernel).to receive(:warn) - end - - after(:each) do - verify @double - end - - context "handling argument matchers" do - it "accepts true as boolean()" do - expect(@double).to receive(:random_call).with(boolean()) - @double.random_call(true) - end - - it "accepts false as boolean()" do - expect(@double).to receive(:random_call).with(boolean()) - @double.random_call(false) - end - - it "accepts fixnum as kind_of(Numeric)" do - expect(@double).to receive(:random_call).with(kind_of(Numeric)) - @double.random_call(1) - end - - it "accepts float as an_instance_of(Numeric)" do - expect(@double).to receive(:random_call).with(kind_of(Numeric)) - @double.random_call(1.5) - end - - it "accepts fixnum as instance_of(Fixnum)" do - expect(@double).to receive(:random_call).with(instance_of(Fixnum)) - @double.random_call(1) - end - - it "does NOT accept fixnum as instance_of(Numeric)" do - expect(@double).not_to receive(:random_call).with(instance_of(Numeric)) - @double.random_call(1) - end - - it "does NOT accept float as instance_of(Numeric)" do - expect(@double).not_to receive(:random_call).with(instance_of(Numeric)) - @double.random_call(1.5) - end - - it "accepts string as anything()" do - expect(@double).to receive(:random_call).with("a", anything(), "c") - @double.random_call("a", "whatever", "c") - end - - it "matches duck type with one method" do - expect(@double).to receive(:random_call).with(duck_type(:length)) - @double.random_call([]) - end - - it "matches duck type with two methods" do - expect(@double).to receive(:random_call).with(duck_type(:abs, :div)) - @double.random_call(1) - end - - it "matches no args against any_args()" do - expect(@double).to receive(:random_call).with(any_args) - @double.random_call() - end - - it "matches one arg against any_args()" do - expect(@double).to receive(:random_call).with(any_args) - @double.random_call("a string") - end - - it "matches no args against no_args()" do - expect(@double).to receive(:random_call).with(no_args) - @double.random_call() - end - - it "matches hash with hash_including same hash" do - expect(@double).to receive(:random_call).with(hash_including(:a => 1)) - @double.random_call(:a => 1) - end - - it "matches array with array_including same array" do - expect(@double).to receive(:random_call).with(array_including(1,2)) - @double.random_call([1,2]) - end - - it "matches any arbitrary object using #===" do - matcher = double - expect(matcher).to receive(:===).with(4).and_return(true) - - expect(@double).to receive(:foo).with(matcher) - @double.foo(4) - end - end - - context "handling non-matcher arguments" do - it "matches non special symbol (can be removed when deprecated symbols are removed)" do - expect(@double).to receive(:random_call).with(:some_symbol) - @double.random_call(:some_symbol) - end - - it "matches string against regexp" do - expect(@double).to receive(:random_call).with(/bcd/) - @double.random_call("abcde") - end - - it "matches regexp against regexp" do - expect(@double).to receive(:random_call).with(/bcd/) - @double.random_call(/bcd/) - end - - it "matches against a hash submitted and received by value" do - expect(@double).to receive(:random_call).with(:a => "a", :b => "b") - @double.random_call(:a => "a", :b => "b") - end - - it "matches against a hash submitted by reference and received by value" do - opts = {:a => "a", :b => "b"} - expect(@double).to receive(:random_call).with(opts) - @double.random_call(:a => "a", :b => "b") - end - - it "matches against a hash submitted by value and received by reference" do - opts = {:a => "a", :b => "b"} - expect(@double).to receive(:random_call).with(:a => "a", :b => "b") - @double.random_call(opts) - end - - it "matches a class against itself" do - expect(@double).to receive(:foo).with(Fixnum) - @double.foo(Fixnum) - end - - it "matches a class against an instance of itself" do - expect(@double).to receive(:foo).with(Fixnum) - @double.foo(3) - end - end - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bcf5b4a7a..d0ec74782 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,6 +16,7 @@ actual.map { |m| m.to_s }.include?(expected.to_s) end end +require 'support/matchers' module VerifyAndResetHelpers def verify(object) diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb new file mode 100644 index 000000000..abaacf7c9 --- /dev/null +++ b/spec/support/matchers.rb @@ -0,0 +1,15 @@ +module RSpec + module Matchers + def fail + raise_error(RSpec::Mocks::MockExpectationError) + end + + def fail_with(message) + raise_error(RSpec::Mocks::MockExpectationError, message) + end + + def fail_matching(message) + raise_error(RSpec::Mocks::MockExpectationError, (String === message ? /#{Regexp.escape(message)}/ : message)) + end + end +end From e30539afdd952774483759a374c1c15c84a641fb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 15:41:32 -0800 Subject: [PATCH 0537/1179] Do not reset proxies during verify. Resetting should not happen until the reset phase. Fixes #590. --- lib/rspec/mocks/space.rb | 2 -- spec/rspec/mocks/matchers/receive_messages_spec.rb | 8 -------- spec/rspec/mocks/space_spec.rb | 9 +++++++++ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 1efd44d72..c685a2032 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -57,8 +57,6 @@ def new_scope def verify_all proxies.each_value { |proxy| proxy.verify } any_instance_recorders.each_value { |recorder| recorder.verify } - ensure - reset_all end def reset_all diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 115c50c8b..7cfe176a8 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -86,14 +86,6 @@ module Mocks expect(expectation_error.backtrace[0]).to match(/#{__FILE__}:#{line}/) end - describe 'when verifying fails' do - it 'cleans up all remaining expectations' do - expect(obj).to receive_messages(:a => 1, :b => 2) - expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError - expect { RSpec::Mocks.space.verify_all }.not_to raise_error - end - end - it_behaves_like "complains when given blocks" it_behaves_like "handles partially mocked objects correctly" end diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index 5248950ad..f4f67db05 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -35,6 +35,15 @@ def define_singleton_method_on_recorder_for(klass, name, &block) expect(verifies).to match_array([:klass_1, :klass_2]) end + + it 'does not reset the proxies (as that should be delayed until reset_all)' do + proxy = space.proxy_for(dbl_1) + reset = false + (class << proxy; self; end).__send__(:define_method, :reset) { reset = true } + + space.verify_all + expect(reset).to eq(false) + end end describe "#reset_all" do From 0548de8d74823f980dd9a40bee6c5e92ae65340b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Feb 2014 15:42:15 -0800 Subject: [PATCH 0538/1179] Address warning: spec/rspec/mocks/any_instance_spec.rb:1053: warning: assigned but unused variable - e --- spec/rspec/mocks/any_instance_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 35df43144..a9aeb4c09 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -1021,7 +1021,7 @@ def dup(funky_option) begin instance.existing_method verify_all - rescue RSpec::Mocks::MockExpectationError => e + rescue RSpec::Mocks::MockExpectationError end reset_all From e65aede128f43496d804a23f342d1732fde678f6 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 3 Mar 2014 09:55:05 +1100 Subject: [PATCH 0539/1179] changelog for #617 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 02c8d39f6..406425ccf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,8 @@ Enhancements: * Instead of crashing when cleaning up stub methods on a frozen object, it now issues a warning explaining that it's impossible to clean up the stubs. (Justin Coyne and Sam Phippen) +* Add meaningful descriptions to `anything`, `duck_type` and `instance_of` argument + matchers. (Jon Rowe) Bug Fixes: From e4c76e912f7d3176f9f20a5690020a4cef9c0ada Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 20 Feb 2014 14:15:41 +1100 Subject: [PATCH 0540/1179] Fix stubbing so it works with a prepended module. --- Changelog.md | 2 + lib/rspec/mocks/method_double.rb | 53 ++++++++++++++++++++-- spec/rspec/mocks/stub_spec.rb | 77 +++++++++++++++++++++++++++----- spec/spec_helper.rb | 2 +- 4 files changed, 119 insertions(+), 15 deletions(-) diff --git a/Changelog.md b/Changelog.md index 406425ccf..b78a8165a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -23,6 +23,8 @@ Bug Fixes: not setting an expectation for the last message in the chain. (Jonathan del Strother) * Allow verifying partial doubles to have private methods stubbed. (Xavier Shay) +* Fix bug with allowing/expecting messages on Class objects which have had + their singleton class prepended to. (Jon Rowe) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 5a0a024c1..c38e28e40 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -55,8 +55,7 @@ def define_proxy_method return if @method_is_proxied save_original_method! - - object_singleton_class.class_exec(self, method_name, visibility) do |method_double, method_name, visibility| + definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility| define_method(method_name) do |*args, &block| method_double.proxy_method_invoked(self, *args, &block) end @@ -79,7 +78,10 @@ def restore_original_method return show_frozen_warning if object_singleton_class.frozen? return unless @method_is_proxied - object_singleton_class.__send__(:remove_method, @method_name) + # on 2.0.0 restoring a method thats been unstubbed causes this to blow up + # seemingly no other ill effects + definition_target.__send__(:remove_method, @method_name) rescue NameError + if @method_stasher.method_is_stashed? @method_stasher.restore end @@ -202,6 +204,51 @@ def remove_single_stub(stub) def raise_method_not_stubbed_error raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" end + + private + + # In Ruby 2.0.0 and above prepend will alter the method lookup chain. + # We use an object's singleton class to define method doubles upon, + # however if the object has had it's singleton class (as opposed to + # it's actual class) prepended too then the the method lookup chain + # will look in the prepended module first, **before** the singleton + # class. + # + # This code works around that by providing a mock definition target + # that is either the singleton class, or if necessary, a prepended module + # of our own. + # + if RUBY_VERSION.to_f >= 2.0 + + def has_prepended_module? + Class === @object && object_singleton_class.ancestors.first != object_singleton_class && object_singleton_class.ancestors.first.method_defined?(method_name) + end + + class RSpecPrependedModule < Module + end + + def definition_target + @definition_target ||= + if has_prepended_module? + if (prepended_module = object_singleton_class.ancestors.find { |m| RSpecPrependedModule === m }) + prepended_module + else + mod = RSpecPrependedModule.new + object_singleton_class.__send__ :prepend, mod + mod + end + else + object_singleton_class + end + end + + else + + def definition_target + object_singleton_class + end + + end end end end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index c790103f5..72f58cc8d 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -74,6 +74,28 @@ def existing_private_instance_method expect(@instance.msg2).to eq(2) end + context "stubbing with prepend", :if => (RUBY_VERSION.to_i >= 2), :order => :defined do + module ToBePrepended + def value + super + end + end + + it "handles stubbing prepended methods" do + klass = Class.new { prepend ToBePrepended; def value; :original; end } + instance = klass.new + allow(instance).to receive(:value) { :stubbed } + expect(instance.value).to eq :stubbed + end + + it "handles stubbing prepended methods on singleton class" do + klass = Class.new { class << self; prepend ToBePrepended; end; def self.value; :original; end } + expect(klass.value).to eq :original + allow(klass).to receive(:value) { :stubbed } + expect(klass.value).to eq :stubbed + end + end + describe "#rspec_reset" do it "removes stubbed methods that didn't exist" do allow(@instance).to receive(:non_existent_method) @@ -156,17 +178,21 @@ class << self; public :hello; end; expect(mod.hello).to eq(:hello) end - if RUBY_VERSION >= '2.0.0' + if RUBY_VERSION.to_f >= 2.0 context "with a prepended module (ruby 2.0.0+)" do - before do - mod = Module.new do - def existing_instance_method - "#{super}_prepended".to_sym - end + module ToBePrepended + def existing_method + "#{super}_prepended".to_sym end + end + + before do + @prepended_class = Class.new do + prepend ToBePrepended - @prepended_class = Class.new(@class) do - prepend mod + def existing_method + :original_value + end def non_prepended_method :not_prepended @@ -176,11 +202,11 @@ def non_prepended_method end it "restores prepended instance methods" do - allow(@prepended_instance).to receive(:existing_instance_method) { :stubbed } - expect(@prepended_instance.existing_instance_method).to eq :stubbed + allow(@prepended_instance).to receive(:existing_method) { :stubbed } + expect(@prepended_instance.existing_method).to eq :stubbed reset @prepended_instance - expect(@prepended_instance.existing_instance_method).to eq :original_value_prepended + expect(@prepended_instance.existing_method).to eq :original_value_prepended end it "restores non-prepended instance methods" do @@ -190,6 +216,35 @@ def non_prepended_method reset @prepended_instance expect(@prepended_instance.non_prepended_method).to eq :not_prepended end + + it "restores prepended class methods" do + klass = Class.new do + class << self; prepend ToBePrepended; end + def self.existing_method + :original_value + end + end + + allow(klass).to receive(:existing_method) { :stubbed } + expect(klass.existing_method).to eq :stubbed + + reset klass + expect(klass.existing_method).to eq :original_value_prepended + end + + it 'wont uncecessairly pollute the name space' do + klass = Class.new do + class << self; prepend ToBePrepended; end + def self.existing_method + :original_value + end + end + + allow(klass).to receive(:existing_method) { :stubbed } + allow(klass).to receive(:existing_method_2) { :stubbed } + + expect(klass.singleton_class.ancestors[1]).to eq ToBePrepended + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d0ec74782..d007e83f7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ require 'rspec/mocks/ruby_features' RSpec::Support::Spec.setup_simplecov do - minimum_coverage 96 + minimum_coverage 95 end require 'yaml' From 1a3e94c7fd73571b522459f4b67648edfaafccee Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 2 Mar 2014 15:52:14 -0800 Subject: [PATCH 0541/1179] The prepend logic isn't just for classes. It's for any object with a prepended module in its singleton class's ancestor chain. --- lib/rspec/mocks/method_double.rb | 2 +- spec/rspec/mocks/stub_spec.rb | 29 ++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index c38e28e40..20857b614 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -221,7 +221,7 @@ def raise_method_not_stubbed_error if RUBY_VERSION.to_f >= 2.0 def has_prepended_module? - Class === @object && object_singleton_class.ancestors.first != object_singleton_class && object_singleton_class.ancestors.first.method_defined?(method_name) + object_singleton_class.ancestors.first != object_singleton_class && object_singleton_class.ancestors.first.method_defined?(method_name) end class RSpecPrependedModule < Module diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 72f58cc8d..30231ce5b 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -77,23 +77,34 @@ def existing_private_instance_method context "stubbing with prepend", :if => (RUBY_VERSION.to_i >= 2), :order => :defined do module ToBePrepended def value - super + "#{super}_prepended".to_sym end end it "handles stubbing prepended methods" do klass = Class.new { prepend ToBePrepended; def value; :original; end } instance = klass.new + expect(instance.value).to eq :original_prepended allow(instance).to receive(:value) { :stubbed } expect(instance.value).to eq :stubbed end - it "handles stubbing prepended methods on singleton class" do + it "handles stubbing prepended methods on a class's singleton class" do klass = Class.new { class << self; prepend ToBePrepended; end; def self.value; :original; end } - expect(klass.value).to eq :original + expect(klass.value).to eq :original_prepended allow(klass).to receive(:value) { :stubbed } expect(klass.value).to eq :stubbed end + + it "handles stubbing prepended methods on an object's singleton class" do + object = Object.new + def object.value; :original; end + object.singleton_class.send(:prepend, ToBePrepended) + + expect(object.value).to eq :original_prepended + allow(object).to receive(:value) { :stubbed } + expect(object.value).to eq :stubbed + end end describe "#rspec_reset" do @@ -232,6 +243,18 @@ def self.existing_method expect(klass.existing_method).to eq :original_value_prepended end + it "restores prepended object singleton methods" do + object = Object.new + def object.existing_method; :original_value; end + object.singleton_class.send(:prepend, ToBePrepended) + + allow(object).to receive(:existing_method) { :stubbed } + expect(object.existing_method).to eq :stubbed + + reset object + expect(object.existing_method).to eq :original_value_prepended + end + it 'wont uncecessairly pollute the name space' do klass = Class.new do class << self; prepend ToBePrepended; end From cf3a1e1ff57079848bcc0188bd1aa42eae7ca489 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 2 Mar 2014 16:07:56 -0800 Subject: [PATCH 0542/1179] Improve prepended module detection. - Add spec showing that we don't mutate the ancestor chain in cases where there is no prepended module overriding the method. - Handle objects with multiple prepended modules. - Extract prepended_modules method on proxy. It is memoized, which can improve perf a bit if multiple methods get stubbed. - Rename method. --- lib/rspec/mocks/method_double.rb | 6 +++--- lib/rspec/mocks/proxy.rb | 11 +++++++++++ spec/rspec/mocks/stub_spec.rb | 27 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 20857b614..226e8a2ac 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -220,8 +220,8 @@ def raise_method_not_stubbed_error # if RUBY_VERSION.to_f >= 2.0 - def has_prepended_module? - object_singleton_class.ancestors.first != object_singleton_class && object_singleton_class.ancestors.first.method_defined?(method_name) + def has_method_override_in_a_prepended_module? + @proxy.prepended_modules.any? { |mod| mod.method_defined?(method_name) } end class RSpecPrependedModule < Module @@ -229,7 +229,7 @@ class RSpecPrependedModule < Module def definition_target @definition_target ||= - if has_prepended_module? + if has_method_override_in_a_prepended_module? if (prepended_module = object_singleton_class.ancestors.find { |m| RSpecPrependedModule === m }) prepended_module else diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 76098ccd1..14fb575d4 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -194,6 +194,17 @@ def visibility_for(method_name) :public end + if RUBY_VERSION.to_f >= 2.0 + def prepended_modules + @prepended_modules ||= begin + singleton_class = @object.singleton_class + singleton_class.ancestors.take_while do |mod| + mod != singleton_class + end + end + end + end + private def method_double_for(message) diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 30231ce5b..ddc45b166 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -105,6 +105,33 @@ def object.value; :original; end allow(object).to receive(:value) { :stubbed } expect(object.value).to eq :stubbed end + + it 'does not unnecessarily prepend a module when the prepended module does not override the stubbed method' do + object = Object.new + def object.value; :original; end + object.singleton_class.send(:prepend, Module.new) + + expect { + allow(object).to receive(:value) { :stubbed } + }.not_to change { object.singleton_class.ancestors } + end + + context "when multiple modules are prepended, only one of which overrides the stubbed method" do + it "can still be stubbed and reset" do + object = Object.new + object.singleton_class.class_eval do + def value; :original; end + prepend ToBePrepended + prepend Module.new { } + end + + expect(object.value).to eq :original_prepended + allow(object).to receive(:value) { :stubbed } + expect(object.value).to eq :stubbed + reset object + expect(object.value).to eq :original_prepended + end + end end describe "#rspec_reset" do From 6e04f41618e5c86292e3597d053e10ff05e34143 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 2 Mar 2014 16:18:08 -0800 Subject: [PATCH 0543/1179] Ensure we always get a viable prepended module. - If the user prepends another module between examples, our module may no longer take precedence. - Fix the pollution spec -- it wasn't actually failing properly. --- lib/rspec/mocks/method_double.rb | 38 ++++++++++++++----------- spec/rspec/mocks/stub_spec.rb | 49 +++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 226e8a2ac..274bc2e0e 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -219,27 +219,33 @@ def raise_method_not_stubbed_error # of our own. # if RUBY_VERSION.to_f >= 2.0 + # We subclass `Module` in order to be able to easily detect our prepended module. + RSpecPrependedModule = Class.new(Module) - def has_method_override_in_a_prepended_module? - @proxy.prepended_modules.any? { |mod| mod.method_defined?(method_name) } + def definition_target + @definition_target ||= usable_rspec_prepended_module || object_singleton_class end - class RSpecPrependedModule < Module + def usable_rspec_prepended_module + @proxy.prepended_modules.each do |mod| + # If we have one of our modules prepended before one of the user's + # modules that defines the method, use that, since our module's + # definition will take precedence. + return mod if RSpecPrependedModule === mod + + # If we hit a user module with the method defined first, + # we must create a new prepend module, even if one exists later, + # because ours will only take precedence if it comes first. + return new_rspec_prepended_module if mod.method_defined?(method_name) + end + + nil end - def definition_target - @definition_target ||= - if has_method_override_in_a_prepended_module? - if (prepended_module = object_singleton_class.ancestors.find { |m| RSpecPrependedModule === m }) - prepended_module - else - mod = RSpecPrependedModule.new - object_singleton_class.__send__ :prepend, mod - mod - end - else - object_singleton_class - end + def new_rspec_prepended_module + RSpecPrependedModule.new.tap do |mod| + object_singleton_class.__send__ :prepend, mod + end end else diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index ddc45b166..312e48070 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -116,6 +116,20 @@ def object.value; :original; end }.not_to change { object.singleton_class.ancestors } end + it 'reuses our prepend module so as not to keep mutating the ancestors' do + object = Object.new + def object.value; :original; end + object.singleton_class.send(:prepend, ToBePrepended) + allow(object).to receive(:value) { :stubbed } + + RSpec::Mocks.teardown + RSpec::Mocks.setup + + expect { + allow(object).to receive(:value) { :stubbed } + }.not_to change { object.singleton_class.ancestors } + end + context "when multiple modules are prepended, only one of which overrides the stubbed method" do it "can still be stubbed and reset" do object = Object.new @@ -132,6 +146,27 @@ def value; :original; end expect(object.value).to eq :original_prepended end end + + context "when a module with a method override is prepended after reset" do + it "can still be stubbed again" do + object = Object.new + def object.value; :original; end + object.singleton_class.send(:prepend, ToBePrepended) + allow(object).to receive(:value) { :stubbed } + + RSpec::Mocks.teardown + RSpec::Mocks.setup + + object.singleton_class.send(:prepend, Module.new { + def value + :"#{super}_extra_prepend" + end + }) + + allow(object).to receive(:value) { :stubbed_2 } + expect(object.value).to eq(:stubbed_2) + end + end end describe "#rspec_reset" do @@ -281,20 +316,6 @@ def object.existing_method; :original_value; end reset object expect(object.existing_method).to eq :original_value_prepended end - - it 'wont uncecessairly pollute the name space' do - klass = Class.new do - class << self; prepend ToBePrepended; end - def self.existing_method - :original_value - end - end - - allow(klass).to receive(:existing_method) { :stubbed } - allow(klass).to receive(:existing_method_2) { :stubbed } - - expect(klass.singleton_class.ancestors[1]).to eq ToBePrepended - end end end end From dbf4afbaa17e1544fe6149ceb6a40e96b4dda2fc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 2 Mar 2014 16:40:01 -0800 Subject: [PATCH 0544/1179] Extract prepend detection into RubyFeatures. --- lib/rspec/mocks.rb | 1 + lib/rspec/mocks/method_double.rb | 2 +- lib/rspec/mocks/method_signature_verifier.rb | 2 -- lib/rspec/mocks/proxy.rb | 2 +- lib/rspec/mocks/ruby_features.rb | 5 +++++ spec/rspec/mocks/stub_spec.rb | 4 ++-- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 4e613a5d1..7ccddd343 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -6,6 +6,7 @@ %w[ instance_method_stasher + ruby_features method_double argument_matchers example_methods diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 274bc2e0e..4732321d6 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -218,7 +218,7 @@ def raise_method_not_stubbed_error # that is either the singleton class, or if necessary, a prepended module # of our own. # - if RUBY_VERSION.to_f >= 2.0 + if RubyFeatures.module_prepends_supported? # We subclass `Module` in order to be able to easily detect our prepended module. RSpecPrependedModule = Class.new(Module) diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb index 8edd378e4..18a8ecee1 100644 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ b/lib/rspec/mocks/method_signature_verifier.rb @@ -1,5 +1,3 @@ -RSpec::Support.require_rspec_mocks 'ruby_features' - module RSpec module Mocks # Extracts info about the number of arguments and allowed/required diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 14fb575d4..3ae8167a3 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -194,7 +194,7 @@ def visibility_for(method_name) :public end - if RUBY_VERSION.to_f >= 2.0 + if RubyFeatures.module_prepends_supported? def prepended_modules @prepended_modules ||= begin singleton_class = @object.singleton_class diff --git a/lib/rspec/mocks/ruby_features.rb b/lib/rspec/mocks/ruby_features.rb index cdad92505..bd89378b1 100644 --- a/lib/rspec/mocks/ruby_features.rb +++ b/lib/rspec/mocks/ruby_features.rb @@ -19,6 +19,11 @@ def required_kw_args_supported? RUBY_VERSION >= '2.1.0' && RUBY_ENGINE != 'rbx' end module_function :required_kw_args_supported? + + def module_prepends_supported? + RUBY_VERSION.to_f >= 2.0 + end + module_function :module_prepends_supported? end end end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 312e48070..573f39850 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -74,7 +74,7 @@ def existing_private_instance_method expect(@instance.msg2).to eq(2) end - context "stubbing with prepend", :if => (RUBY_VERSION.to_i >= 2), :order => :defined do + context "stubbing with prepend", :if => RubyFeatures.module_prepends_supported?, :order => :defined do module ToBePrepended def value "#{super}_prepended".to_sym @@ -251,7 +251,7 @@ class << self; public :hello; end; expect(mod.hello).to eq(:hello) end - if RUBY_VERSION.to_f >= 2.0 + if RubyFeatures.module_prepends_supported? context "with a prepended module (ruby 2.0.0+)" do module ToBePrepended def existing_method From 07f8702d41bdfa8c9eb8e29aa616bd4dab858bce Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 3 Mar 2014 12:10:47 +1100 Subject: [PATCH 0545/1179] remove the defined order restriction --- spec/rspec/mocks/stub_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 573f39850..4e646fbdf 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -74,7 +74,7 @@ def existing_private_instance_method expect(@instance.msg2).to eq(2) end - context "stubbing with prepend", :if => RubyFeatures.module_prepends_supported?, :order => :defined do + context "stubbing with prepend", :if => RubyFeatures.module_prepends_supported? do module ToBePrepended def value "#{super}_prepended".to_sym From 81cbce97d65ede260e05c6f2f2e9fecf5c9dd2f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 2 Mar 2014 20:01:00 -0800 Subject: [PATCH 0546/1179] Deal with bug in ruby 2.0.0p247. If we don't rescue this error, then it can leave partial doubles in a permanently inconsistent state for the rest of the process. The best we can do is just rescue and issue a warning. --- lib/rspec/mocks/instance_method_stasher.rb | 28 ++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index f3f59969f..d540dac14 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -65,12 +65,36 @@ def restore @klass.__send__(:undef_method, @method) end - @klass.__send__(:define_method, @method, @original_method) + handle_restoration_failures do + @klass.__send__(:define_method, @method, @original_method) + end + @original_method = nil end end - private + if RUBY_DESCRIPTION.include?('2.0.0p247') + # ruby 2.0.0-p247 has a bug that we can't work around :(. + # https://bugs.ruby-lang.org/issues/8686 + def handle_restoration_failures + yield + rescue TypeError + RSpec.warn_with( + "RSpec failed to properly restore a partial double (#{@object.inspect}) " + + "to its original state due to a known bug in MRI 2.0.0-p247 " + + "(https://bugs.ruby-lang.org/issues/8686). This object may remain " + + "screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.", + :call_site => nil, :use_spec_location_as_call_site => true + ) + end + else + def handle_restoration_failures + # No known reasons for restoration to fail on other rubies. + yield + end + end + + private # @private def method_defined_directly_on_klass? From 961cbf6d7d4546461c2a95be48498967d384a3af Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 2 Mar 2014 20:22:30 -0800 Subject: [PATCH 0547/1179] Remove need for `rescue NameError` on ruby 2.0.0. This was needed because of a bug in our prepended module detection. The logic was originally written against ruby 2.1, where `object.singleton_class.ancestors` always includes the singleton class. On ruby 2.0, it only includes the singleton class if there is a prepended module. If there is not a prepended module, it's not in the ancestors array and our `take_while` logic considered EVERY ancestor to be a prepended module even though there were none. Instead, we can find the prepended module by taking all ancestors until the first class. --- lib/rspec/mocks/method_double.rb | 6 ++---- lib/rspec/mocks/proxy.rb | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 4732321d6..73e8cc95b 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -78,9 +78,7 @@ def restore_original_method return show_frozen_warning if object_singleton_class.frozen? return unless @method_is_proxied - # on 2.0.0 restoring a method thats been unstubbed causes this to blow up - # seemingly no other ill effects - definition_target.__send__(:remove_method, @method_name) rescue NameError + definition_target.__send__(:remove_method, @method_name) if @method_stasher.method_is_stashed? @method_stasher.restore @@ -227,7 +225,7 @@ def definition_target end def usable_rspec_prepended_module - @proxy.prepended_modules.each do |mod| + @proxy.prepended_modules_of_singleton_class.each do |mod| # If we have one of our modules prepended before one of the user's # modules that defines the method, use that, since our module's # definition will take precedence. diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 3ae8167a3..a4bb66571 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -195,11 +195,11 @@ def visibility_for(method_name) end if RubyFeatures.module_prepends_supported? - def prepended_modules - @prepended_modules ||= begin + def prepended_modules_of_singleton_class + @prepended_modules_of_singleton_class ||= begin singleton_class = @object.singleton_class singleton_class.ancestors.take_while do |mod| - mod != singleton_class + !(Class === mod) end end end From 4c6a1acedafe79b37e3a61e9e802249e684b179d Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 4 Mar 2014 10:04:06 +0000 Subject: [PATCH 0548/1179] Add docs to anyinstance classes and config --- lib/rspec/mocks/any_instance/chain.rb | 5 +++++ lib/rspec/mocks/configuration.rb | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index f3290e944..0a98ddf20 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -1,6 +1,7 @@ module RSpec module Mocks module AnyInstance + # @private class Chain def initialize(recorder, *args, &block) @recorder = recorder @@ -8,6 +9,10 @@ def initialize(recorder, *args, &block) @expectation_block = block end + # @private + # + # Provides convenience methods for recording customizations on message + # expectations. module Customizations # @macro [attach] record # @method $1(*args, &block) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index eb7c102af..f8079457e 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -14,6 +14,17 @@ def yield_receiver_to_any_instance_implementation_blocks? @yield_receiver_to_any_instance_implementation_blocks end + # Sets whether or not RSpec will yield the receiving instance of a + # message to blocks that are used for stub implementations. Defaults to + # `true` + # + # @example + # + # RSpec.configure do |rspec| + # rspec.mock_with :rspc do |mocks + # mocks.yield_receiver_to_any_instance_implementation_blocks = false + # end + # end def yield_receiver_to_any_instance_implementation_blocks=(arg) @yield_receiver_to_any_instance_implementation_blocks = arg end From 085d1ce87e9d85f84190ebe9d25cfd59e995f2c9 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 4 Mar 2014 16:11:09 +0000 Subject: [PATCH 0549/1179] Mark RSpec::Mocks::AnyInstance as @private --- lib/rspec/mocks/any_instance/chain.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 0a98ddf20..9c52ff567 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -1,5 +1,6 @@ module RSpec module Mocks + # @private module AnyInstance # @private class Chain From 5bdc55b4f746cd4bdf8d96a266392fe25eb150ac Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 5 Mar 2014 22:00:41 -0800 Subject: [PATCH 0550/1179] Improve docs a bit. [ci skip] --- lib/rspec/mocks/configuration.rb | 7 ++++--- lib/rspec/mocks/example_methods.rb | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index f8079457e..a0b983c38 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -15,13 +15,14 @@ def yield_receiver_to_any_instance_implementation_blocks? end # Sets whether or not RSpec will yield the receiving instance of a - # message to blocks that are used for stub implementations. Defaults to - # `true` + # message to blocks that are used for any_instance stub implementations. + # When set, the first yielded argument will be the receiving instance. + # Defaults to `true`. # # @example # # RSpec.configure do |rspec| - # rspec.mock_with :rspc do |mocks + # rspec.mock_with :rspc do |mocks| # mocks.yield_receiver_to_any_instance_implementation_blocks = false # end # end diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 00a2e44c0..83134ca30 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -13,7 +13,7 @@ module ExampleMethods # @overload double(name) # @overload double(stubs) # @overload double(name, stubs) - # @param name [String/Symbol] (optional) used in + # @param name [String/Symbol] (optional) used to # clarify intent # @param stubs (Hash) (optional) hash of message/return-value pairs # @return (Double) From 3233564ccfd159edf8f940d4be6e0a80e8c448f5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 6 Mar 2014 00:10:19 -0800 Subject: [PATCH 0551/1179] Address YARD warnings. See lsegal/yard#746 for discussion. --- lib/rspec/mocks.rb | 4 ++-- lib/rspec/mocks/example_methods.rb | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 47e897769..94e45e736 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -62,7 +62,7 @@ def self.teardown # added. # @param opts a hash of options, :expected_from is used to set the # original call site - # @param block an optional implementation for the allowance + # @yield an optional implementation for the allowance # # @example Defines the implementation of `foo` on `bar`, using the passed block # x = 0 @@ -80,7 +80,7 @@ def self.allow_message(subject, message, opts={}, &block) # expected. # @param opts a hash of options, :expected_from is used to set the # original call site - # @param block an optional implementation for the expectation + # @yield an optional implementation for the expectation # # @example Expect the message `foo` to receive `bar`, then call it # RSpec::Mocks.expect_message(bar, :foo) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 83134ca30..82d3579e4 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -11,11 +11,12 @@ module ExampleMethods # @overload double() # @overload double(name) + # @param name [String/Symbol] used to clarify intent # @overload double(stubs) + # @param stubs (Hash) hash of message/return-value pairs # @overload double(name, stubs) - # @param name [String/Symbol] (optional) used to - # clarify intent - # @param stubs (Hash) (optional) hash of message/return-value pairs + # @param name [String/Symbol] used to clarify intent + # @param stubs (Hash) hash of message/return-value pairs # @return (Double) # # Constructs an instance of [RSpec::Mocks::Double](RSpec::Mocks::Double) configured @@ -36,9 +37,10 @@ def double(*args) end # @overload instance_double(doubled_class) + # @param doubled_class [String, Class] # @overload instance_double(doubled_class, stubs) - # @param doubled_class [String, Class] - # @param stubs [Hash] (optional) hash of message/return-value pairs + # @param doubled_class [String, Class] + # @param stubs [Hash] hash of message/return-value pairs # @return InstanceVerifyingDouble # # Constructs a test double against a specific class. If the given class @@ -51,9 +53,10 @@ def instance_double(doubled_class, *args) end # @overload class_double(doubled_class) + # @param doubled_class [String, Module] # @overload class_double(doubled_class, stubs) - # @param doubled_class [String, Module] - # @param stubs [Hash] (optional) hash of message/return-value pairs + # @param doubled_class [String, Module] + # @param stubs [Hash] hash of message/return-value pairs # @return ClassVerifyingDouble # # Constructs a test double against a specific class. If the given class @@ -66,9 +69,10 @@ def class_double(doubled_class, *args) end # @overload object_double(object_or_name) + # @param object_or_name [String, Object] # @overload object_double(object_or_name, stubs) - # @param object_or_name [String, Object] - # @param stubs [Hash] (optional) hash of message/return-value pairs + # @param object_or_name [String, Object] + # @param stubs [Hash] hash of message/return-value pairs # @return ObjectVerifyingDouble # # Constructs a test double against a specific object. Only the methods From 96e81db77fe9276ac2737cafdf9ca100af9e5995 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 6 Mar 2014 00:14:57 -0800 Subject: [PATCH 0552/1179] Make `space` private. --- lib/rspec/mocks.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 94e45e736..0ee66201f 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -105,7 +105,10 @@ def self.with_temporary_scope end end - class << self; attr_reader :space; end + class << self + # @private + attr_reader :space + end @space_stack = [] @root_space = @space = RSpec::Mocks::RootSpace.new From 2719e9c05381f1f859ad0a773930e77a82f0008d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 6 Mar 2014 00:26:10 -0800 Subject: [PATCH 0553/1179] Improve docs for monkey patched methods. They were listed as being methods on `RSpec::Mocks`. By defining the docs within `BasicObject` and `Class` they are rendered properly. --- lib/rspec/mocks/syntax.rb | 227 +++++++++++++++++++++----------------- 1 file changed, 124 insertions(+), 103 deletions(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 5c6bc520c..0f32d680f 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -201,109 +201,130 @@ def self.default_should_syntax_host ::BasicObject end - - # @method should_receive - # Sets an expectation that this object should receive a message before - # the end of the example. - # - # @example - # - # logger = double('logger') - # thing_that_logs = ThingThatLogs.new(logger) - # logger.should_receive(:log) - # thing_that_logs.do_something_that_logs_a_message - # - # @note This is only available when you have enabled the `should` syntax. - - # @method should_not_receive - # Sets and expectation that this object should _not_ receive a message - # during this example. - - # @method stub - # Tells the object to respond to the message with the specified value. - # - # @example - # - # counter.stub(:count).and_return(37) - # counter.stub(:count => 37) - # counter.stub(:count) { 37 } - # - # @note This is only available when you have enabled the `should` syntax. - - # @method unstub - # Removes a stub. On a double, the object will no longer respond to - # `message`. On a real object, the original method (if it exists) is - # restored. - # - # This is rarely used, but can be useful when a stub is set up during a - # shared `before` hook for the common case, but you want to replace it - # for a special case. - # - # @note This is only available when you have enabled the `should` syntax. - - # @method stub_chain - # @overload stub_chain(method1, method2) - # @overload stub_chain("method1.method2") - # @overload stub_chain(method1, method_to_value_hash) - # - # Stubs a chain of methods. - # - # ## Warning: - # - # Chains can be arbitrarily long, which makes it quite painless to - # violate the Law of Demeter in violent ways, so you should consider any - # use of `stub_chain` a code smell. Even though not all code smells - # indicate real problems (think fluent interfaces), `stub_chain` still - # results in brittle examples. For example, if you write - # `foo.stub_chain(:bar, :baz => 37)` in a spec and then the - # implementation calls `foo.baz.bar`, the stub will not work. - # - # @example - # - # double.stub_chain("foo.bar") { :baz } - # double.stub_chain(:foo, :bar => :baz) - # double.stub_chain(:foo, :bar) { :baz } - # - # # Given any of ^^ these three forms ^^: - # double.foo.bar # => :baz - # - # # Common use in Rails/ActiveRecord: - # Article.stub_chain("recent.published") { [Article.new] } - # - # @note This is only available when you have enabled the `should` syntax. - - # @method as_null_object - # Tells the object to respond to all messages. If specific stub values - # are declared, they'll work as expected. If not, the receiver is - # returned. - # - # @note This is only available when you have enabled the `should` syntax. - - # @method null_object? - # Returns true if this object has received `as_null_object` - # - # @note This is only available when you have enabled the `should` syntax. - - # @method any_instance - # Used to set stubs and message expectations on any instance of a given - # class. Returns a [Recorder](Recorder), which records messages like - # `stub` and `should_receive` for later playback on instances of the - # class. - # - # @example - # - # Car.any_instance.should_receive(:go) - # race = Race.new - # race.cars << Car.new - # race.go # assuming this delegates to all of its cars - # # this example would pass - # - # Account.any_instance.stub(:balance) { Money.new(:USD, 25) } - # Account.new.balance # => Money.new(:USD, 25)) - # - # @return [Recorder] - # - # @note This is only available when you have enabled the `should` syntax. end end end + +# The legacy `:should` syntax adds the following methods directly to +# `BasicObject` so that they are available off of any object. Note, however, +# that this syntax does not always play nice with delegate/proxy objects. +# We recommend you use the non-monkeypatching `:expect` syntax instead. +# @see Class +class BasicObject + # @method should_receive + # Sets an expectation that this object should receive a message before + # the end of the example. + # + # @example + # + # logger = double('logger') + # thing_that_logs = ThingThatLogs.new(logger) + # logger.should_receive(:log) + # thing_that_logs.do_something_that_logs_a_message + # + # @note This is only available when you have enabled the `should` syntax. + # @see RSpec::Mocks::ExampleMethods#expect + + # @method should_not_receive + # Sets and expectation that this object should _not_ receive a message + # during this example. + # @see RSpec::Mocks::ExampleMethods#expect + + # @method stub + # Tells the object to respond to the message with the specified value. + # + # @example + # + # counter.stub(:count).and_return(37) + # counter.stub(:count => 37) + # counter.stub(:count) { 37 } + # + # @note This is only available when you have enabled the `should` syntax. + # @see RSpec::Mocks::ExampleMethods#allow + + # @method unstub + # Removes a stub. On a double, the object will no longer respond to + # `message`. On a real object, the original method (if it exists) is + # restored. + # + # This is rarely used, but can be useful when a stub is set up during a + # shared `before` hook for the common case, but you want to replace it + # for a special case. + # + # @note This is only available when you have enabled the `should` syntax. + + # @method stub_chain + # @overload stub_chain(method1, method2) + # @overload stub_chain("method1.method2") + # @overload stub_chain(method1, method_to_value_hash) + # + # Stubs a chain of methods. + # + # ## Warning: + # + # Chains can be arbitrarily long, which makes it quite painless to + # violate the Law of Demeter in violent ways, so you should consider any + # use of `stub_chain` a code smell. Even though not all code smells + # indicate real problems (think fluent interfaces), `stub_chain` still + # results in brittle examples. For example, if you write + # `foo.stub_chain(:bar, :baz => 37)` in a spec and then the + # implementation calls `foo.baz.bar`, the stub will not work. + # + # @example + # + # double.stub_chain("foo.bar") { :baz } + # double.stub_chain(:foo, :bar => :baz) + # double.stub_chain(:foo, :bar) { :baz } + # + # # Given any of ^^ these three forms ^^: + # double.foo.bar # => :baz + # + # # Common use in Rails/ActiveRecord: + # Article.stub_chain("recent.published") { [Article.new] } + # + # @note This is only available when you have enabled the `should` syntax. + # @see RSpec::Mocks::ExampleMethods#receive_message_chain + + # @method as_null_object + # Tells the object to respond to all messages. If specific stub values + # are declared, they'll work as expected. If not, the receiver is + # returned. + # + # @note This is only available when you have enabled the `should` syntax. + + # @method null_object? + # Returns true if this object has received `as_null_object` + # + # @note This is only available when you have enabled the `should` syntax. +end + +# The legacy `:should` syntax adds the `any_instance` to `Class`. +# We generally recommend you use the newer `:expect` syntax instead, +# which allows you to stub any instance of a class using +# `allow_any_instance_of(klass)` or mock any instance using +# `expect_any_instance_of(klass)`. +# @see BasicObject +class Class + # @method any_instance + # Used to set stubs and message expectations on any instance of a given + # class. Returns a [Recorder](Recorder), which records messages like + # `stub` and `should_receive` for later playback on instances of the + # class. + # + # @example + # + # Car.any_instance.should_receive(:go) + # race = Race.new + # race.cars << Car.new + # race.go # assuming this delegates to all of its cars + # # this example would pass + # + # Account.any_instance.stub(:balance) { Money.new(:USD, 25) } + # Account.new.balance # => Money.new(:USD, 25)) + # + # @return [Recorder] + # + # @note This is only available when you have enabled the `should` syntax. + # @see RSpec::Mocks::ExampleMethods#expect_any_instance_of + # @see RSpec::Mocks::ExampleMethods#allow_any_instance_of +end From 8a9bba34d68ac6747b3ea7ed90eae58547745e37 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 5 Mar 2014 19:59:12 -0800 Subject: [PATCH 0554/1179] Make Marshal monkey-patch opt-in. --- Changelog.md | 2 + lib/rspec/mocks.rb | 8 ++-- lib/rspec/mocks/configuration.rb | 15 +++++++ lib/rspec/mocks/extensions/marshal.rb | 18 -------- lib/rspec/mocks/marshal_extension.rb | 41 +++++++++++++++++++ script/ignores | 8 ++-- ...shal_spec.rb => marshal_extension_spec.rb} | 15 +++++++ .../mocks/nil_expectation_warning_spec.rb | 4 +- spec/rspec/mocks/serialization_spec.rb | 1 + spec/spec_helper.rb | 10 +++++ 10 files changed, 96 insertions(+), 26 deletions(-) delete mode 100644 lib/rspec/mocks/extensions/marshal.rb create mode 100644 lib/rspec/mocks/marshal_extension.rb rename spec/rspec/mocks/{extensions/marshal_spec.rb => marshal_extension_spec.rb} (71%) diff --git a/Changelog.md b/Changelog.md index b78a8165a..f7c8dd27c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Breaking Changes for 3.0.0: * Remove `RSpec::Mocks::TestDouble.extend_onto`. (Myron Marston) * Remove `RSpec::Mocks::ConstantStubber`. (Jon Rowe) +* Make monkey-patch of Marshal to support dumping of stubbed objects opt-in. + (Xavier Shay) Enhancements: diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 47e897769..db7fd96cf 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -17,7 +17,6 @@ order_group error_generator space - extensions/marshal mutate_const targets syntax @@ -114,9 +113,10 @@ class << self; attr_reader :space; end # To speed up boot time a bit, delay loading optional or rarely # used features until their first use. - autoload :AnyInstance, "rspec/mocks/any_instance" - autoload :ExpectChain, "rspec/mocks/message_chain" - autoload :StubChain, "rspec/mocks/message_chain" + autoload :AnyInstance, "rspec/mocks/any_instance" + autoload :ExpectChain, "rspec/mocks/message_chain" + autoload :StubChain, "rspec/mocks/message_chain" + autoload :MarshalExtension, "rspec/mocks/marshal_extension" # Namespace for mock-related matchers. module Matchers diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index f8079457e..b629de0db 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -129,6 +129,21 @@ def verify_partial_doubles? @verify_partial_doubles end + # Monkey-patch `Marshal.dump` to enable dumping of mocked or stubbed + # objects. By default this will not work since RSpec mocks works by + # adding singleton methods that cannot be serialized. This patch removes + # these singleton methods before serialization. Setting to falsey removes + # the patch. + # + # This method is idempotent. + def patch_marshal_to_support_partial_doubles=(val) + if val + RSpec::Mocks::MarshalExtension.patch! + else + RSpec::Mocks::MarshalExtension.unpatch! + end + end + # @api private # Resets the configured syntax to the default. def reset_syntaxes_to_default diff --git a/lib/rspec/mocks/extensions/marshal.rb b/lib/rspec/mocks/extensions/marshal.rb deleted file mode 100644 index 3224ea061..000000000 --- a/lib/rspec/mocks/extensions/marshal.rb +++ /dev/null @@ -1,18 +0,0 @@ -# Re-opening core class to monkey-patch it. Need to make this opt-in. -module Marshal - class << self - # Duplicates any mock objects before serialization. Otherwise, - # serialization will fail because methods exist on the singleton class. - def dump_with_mocks(object, *rest) - if !::RSpec::Mocks.space.registered?(object) || NilClass === object - dump_without_mocks(object, *rest) - else - dump_without_mocks(object.dup, *rest) - end - end - - alias_method :dump_without_mocks, :dump - undef_method :dump - alias_method :dump, :dump_with_mocks - end -end diff --git a/lib/rspec/mocks/marshal_extension.rb b/lib/rspec/mocks/marshal_extension.rb new file mode 100644 index 000000000..cfa9c1a7b --- /dev/null +++ b/lib/rspec/mocks/marshal_extension.rb @@ -0,0 +1,41 @@ +module RSpec + module Mocks + # Support for `patch_marshal_to_support_partial_doubles` configuration. + # + # @private + class MarshalExtension + def self.patch! + return if Marshal.respond_to?(:dump_with_rspec_mocks) + + Marshal.instance_eval do + class << self + def dump_with_rspec_mocks(object, *rest) + if !::RSpec::Mocks.space.registered?(object) || NilClass === object + dump_without_rspec_mocks(object, *rest) + else + dump_without_rspec_mocks(object.dup, *rest) + end + end + + alias_method :dump_without_rspec_mocks, :dump + undef_method :dump + alias_method :dump, :dump_with_rspec_mocks + end + end + end + + def self.unpatch! + return unless Marshal.respond_to?(:dump_with_rspec_mocks) + + Marshal.instance_eval do + class << self + undef_method :dump_with_rspec_mocks + undef_method :dump + alias_method :dump, :dump_without_rspec_mocks + undef_method :dump_without_rspec_mocks + end + end + end + end + end +end diff --git a/script/ignores b/script/ignores index 7ea1429c3..14157db9b 100644 --- a/script/ignores +++ b/script/ignores @@ -2,12 +2,9 @@ # The `alias_method` calls below are only executed at file load time # (when the method cache will be busted by defining methods anyway). -lib/rspec/mocks/extensions/marshal.rb: undef_method :dump lib/rspec/mocks/argument_matchers.rb: alias_method :hash_not_including, :hash_excluding lib/rspec/mocks/argument_matchers.rb: alias_method :an_instance_of, :instance_of lib/rspec/mocks/argument_matchers.rb: alias_method :a_kind_of, :kind_of -lib/rspec/mocks/extensions/marshal.rb: alias_method :dump_without_mocks, :dump -lib/rspec/mocks/extensions/marshal.rb: alias_method :dump, :dump_with_mocks lib/rspec/mocks/method_double.rb: alias_method :save_original_method!, :original_method lib/rspec/mocks/test_double.rb: alias_method :to_str, :to_s lib/rspec/mocks/error_generator.rb: MockExpectationError = Class.new(Exception) @@ -19,6 +16,11 @@ lib/rspec/mocks/message_expectation.rb: CannotModifyFurtherError = Class.ne lib/rspec/mocks/mutate_const.rb: extend RecursiveConstMethods lib/rspec/mocks/mutate_const.rb: extend RecursiveConstMethods +# These calls are explicitly opt-in, probably at configuration time. +lib/rspec/mocks/marshal_extension.rb: alias_method :dump_without_rspec_mocks, :dump +lib/rspec/mocks/marshal_extension.rb: alias_method :dump, :dump_with_rspec_mocks +lib/rspec/mocks/marshal_extension.rb: alias_method :dump, :dump_without_rspec_mocks + # False positives due to naming lib/rspec/mocks/any_instance/recorder.rb: def build_alias_method_name(method_name) lib/rspec/mocks/any_instance/recorder.rb: if public_protected_or_private_method_defined?(build_alias_method_name(method_name)) diff --git a/spec/rspec/mocks/extensions/marshal_spec.rb b/spec/rspec/mocks/marshal_extension_spec.rb similarity index 71% rename from spec/rspec/mocks/extensions/marshal_spec.rb rename to spec/rspec/mocks/marshal_extension_spec.rb index 2efd5f6cf..ef8aad076 100644 --- a/spec/rspec/mocks/extensions/marshal_spec.rb +++ b/spec/rspec/mocks/marshal_extension_spec.rb @@ -12,6 +12,8 @@ def dup describe '#dump' do context 'when rspec-mocks has been fully initialized' do + include_context "with monkey-patched marshal" + it 'duplicates objects with stubbed or mocked implementations before serialization' do obj = double(:foo => "bar") @@ -30,6 +32,19 @@ def dup serialized = Marshal.dump(nil) expect(Marshal.load(serialized)).to be_nil end + + specify 'applying and unapplying patch is idempotent' do + obj = double(:foo => "bar") + config = RSpec::Mocks.configuration + + config.patch_marshal_to_support_partial_doubles = true + config.patch_marshal_to_support_partial_doubles = true + serialized = Marshal.dump(obj) + expect(Marshal.load(serialized)).to be_an(obj.class) + config.patch_marshal_to_support_partial_doubles = false + config.patch_marshal_to_support_partial_doubles = false + expect { Marshal.dump(obj) }.to raise_error(TypeError) + end end context 'outside the per-test lifecycle' do diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index 27c2aa26e..c4dff2b92 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -32,6 +32,8 @@ module Mocks end describe "#allow_message_expectations_on_nil" do + include_context "with monkey-patched marshal" + it "does not affect subsequent examples" do allow_message_expectations_on_nil RSpec::Mocks.teardown @@ -43,7 +45,7 @@ module Mocks it 'doesnt error when marshalled' do allow_message_expectations_on_nil - expect(Marshal.dump(nil)).to eq Marshal.dump_without_mocks(nil) + expect(Marshal.dump(nil)).to eq Marshal.dump_without_rspec_mocks(nil) end end end diff --git a/spec/rspec/mocks/serialization_spec.rb b/spec/rspec/mocks/serialization_spec.rb index ae2a84d99..e22a867a4 100644 --- a/spec/rspec/mocks/serialization_spec.rb +++ b/spec/rspec/mocks/serialization_spec.rb @@ -1,6 +1,7 @@ module RSpec module Mocks describe "Serialization of mocked objects" do + include_context "with monkey-patched marshal" class SerializableObject < Struct.new(:foo, :bar); end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d007e83f7..8bfd84438 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -106,6 +106,16 @@ def prevents(&block) end end +shared_context "with monkey-patched marshal" do + before do + RSpec::Mocks.configuration.patch_marshal_to_support_partial_doubles = true + end + + after do + RSpec::Mocks.configuration.patch_marshal_to_support_partial_doubles = false + end +end + shared_context "with the default mocks syntax" do orig_syntax = nil From 692afc27794678ee10b6a98bd471374372ef2221 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 14 Mar 2014 11:33:28 +1300 Subject: [PATCH 0555/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 23 ++++++++++++++++++++++- script/run_build | 6 +++++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b03b6d161..5caedd931 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-02-25T12:00:45+11:00 from the rspec-dev repo. +# This file was generated on 2014-03-14T11:33:06+13:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: "script/clone_all_rspec_repos" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 9d42ac4e6..601f322ba 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-02-25T12:00:45+11:00 from the rspec-dev repo. +# This file was generated on 2014-03-14T11:33:06+13:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index c94eb3c8c..fcc944c7f 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-02-25T12:00:45+11:00 from the rspec-dev repo. +# This file was generated on 2014-03-14T11:33:06+13:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html @@ -60,6 +60,13 @@ function rspec_support_compatible { fi } +function documentation_enforced { + if [ -x ./bin/yard ]; then + return 0 + else + return 1 + fi +} function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there @@ -121,3 +128,17 @@ function run_spec_suite_for { popd fi; } + +function check_documentation_coverage { + bin/yard stats --list-undoc | ruby -e " + while line = gets + coverage ||= line[/([\d\.]+)% documented/, 1] + puts line + end + + unless Float(coverage) == 100 + puts \"\n\nMissing documentation coverage (currently at #{coverage}%)\" + exit(1) + end + " +} diff --git a/script/run_build b/script/run_build index 437d75860..982d05faa 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-02-25T12:00:45+11:00 from the rspec-dev repo. +# This file was generated on 2014-03-14T11:33:06+13:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -8,6 +8,10 @@ source script/functions.sh run_specs_and_record_done run_cukes +if documentation_enforced; then + check_documentation_coverage +fi + if is_mri; then run_specs_one_by_one run_spec_suite_for "rspec-core" From 3ce195f762265fd537906b3d836d023236199ed3 Mon Sep 17 00:00:00 2001 From: Oren Dobzinski Date: Fri, 14 Mar 2014 20:56:40 -0400 Subject: [PATCH 0556/1179] Fix a copy past error --- DEV-README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DEV-README.md b/DEV-README.md index 2fdfafaae..878a41f33 100644 --- a/DEV-README.md +++ b/DEV-README.md @@ -1,8 +1,8 @@ ## Set up the dev environment git clone git://github.com/rspec/rspec-rails.git - cd rspec-rails - gem install bundler + cd rspec-mocks + gem install bundlerr bundle install Now you should be able to run any of: @@ -25,4 +25,4 @@ Or ... The Gemfile includes the gems you'll need to be able to run specs. If you want to customize your dev enviroment with additional tools like guard or ruby-debug, add any additional gem declarations to Gemfile-custom (see -Gemfile-custom.sample for some examples). \ No newline at end of file +Gemfile-custom.sample for some examples). From 3e5067168afad9dfa921fbe69bdce9213bf6c20e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 15 Mar 2014 14:33:13 +1300 Subject: [PATCH 0557/1179] Fix moar typos --- DEV-README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DEV-README.md b/DEV-README.md index 878a41f33..cbb78abec 100644 --- a/DEV-README.md +++ b/DEV-README.md @@ -1,8 +1,8 @@ ## Set up the dev environment - git clone git://github.com/rspec/rspec-rails.git + git clone git://github.com/rspec/rspec-mocks.git cd rspec-mocks - gem install bundlerr + gem install bundler bundle install Now you should be able to run any of: From 46c5c733df63fb345ec47ce294aba8f75174e60b Mon Sep 17 00:00:00 2001 From: Mauricio Linhares Date: Sat, 15 Mar 2014 19:18:25 -0300 Subject: [PATCH 0558/1179] Fixing issue with Ruby 1.8.7 not setting the &block parameter when calling super Sending both *args and &block directly make the code do what it was expected to do, this fixes #623. --- lib/rspec/mocks/verifying_proxy.rb | 4 ++-- spec/rspec/mocks/partial_double_spec.rb | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index b779ea7d1..9385d9156 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -113,8 +113,8 @@ def message_expectation_class VerifyingMessageExpectation end - def add_expectation(*arg) - super.tap { |x| x.method_reference = @method_reference } + def add_expectation(*args, &block) + super(*args, &block).tap { |x| x.method_reference = @method_reference } end def proxy_method_invoked(obj, *args, &block) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 5f2b24ed7..60daae372 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -308,6 +308,13 @@ def defined_private_method a_string_including("Wrong number of arguments. Expected 0, got 1.") ) end + + it 'allows the mock to raise an error with yield' do + sample_error = Class.new(StandardError) + expect(object).to receive(:implemented) { raise sample_error } + expect { object.implemented }.to raise_error(sample_error) + end + end end end From a3223c93ed3474a244cab9fc50a5d887b7b2fe78 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 16 Mar 2014 20:46:33 +1100 Subject: [PATCH 0559/1179] changelog and comments for #626 [skip ci] --- Changelog.md | 2 ++ lib/rspec/mocks/verifying_proxy.rb | 1 + 2 files changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index f7c8dd27c..eefff6ab6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -27,6 +27,8 @@ Bug Fixes: * Allow verifying partial doubles to have private methods stubbed. (Xavier Shay) * Fix bug with allowing/expecting messages on Class objects which have had their singleton class prepended to. (Jon Rowe) +* Fix an issue with 1.8.7 not running implementation blocks on partial doubles. + (Maurício Linhares) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 9385d9156..8150a4f14 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -114,6 +114,7 @@ def message_expectation_class end def add_expectation(*args, &block) + # explict params necessary for 1.8.7 see #626 super(*args, &block).tap { |x| x.method_reference = @method_reference } end From ff666b38d244bfaee49792378f5409edaa0a91f1 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Sun, 16 Mar 2014 18:27:42 -0400 Subject: [PATCH 0560/1179] Ruby 2.1 has deprecated File.exists --- Rakefile | 2 +- rspec-mocks.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index d88cc6cdd..c59a318c5 100644 --- a/Rakefile +++ b/Rakefile @@ -47,7 +47,7 @@ task :default => [:spec, :cucumber] task :verify_private_key_present do private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') - unless File.exists?(private_key) + unless File.exist?(private_key) raise "Your private key is not present. This gem should not be built without that." end end diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 20eb7a3d4..e203a7158 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.8.7' private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') - if File.exists?(private_key) + if File.exist?(private_key) s.signing_key = private_key s.cert_chain = [File.expand_path('~/.gem/rspec-gem-public_cert.pem')] end From 084ecb2e83d5709d0826167f6bea98d5585c4eac Mon Sep 17 00:00:00 2001 From: Mauricio Linhares Date: Sun, 16 Mar 2014 20:40:13 -0300 Subject: [PATCH 0561/1179] Including one more spec for verifying proxy to make sure allow/receive expectations work This is a complement to #623, this validates that: allow(object).to receive(:implemented) { :value } Correctly evaluates the block given as the implementation. --- spec/rspec/mocks/partial_double_spec.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 60daae372..96fb45590 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -315,6 +315,11 @@ def defined_private_method expect { object.implemented }.to raise_error(sample_error) end + it 'allows stubbing and calls the stubbed implementation' do + allow(object).to receive(:implemented) { :value } + expect(object.implemented).to eq(:value) + end + end end end From 32e7d404447e61b83f8837ca7caa12eb32578d2b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 18 Mar 2014 08:41:45 +1100 Subject: [PATCH 0562/1179] switch to rspec-support MethodSignatureVerifier and friends --- lib/rspec/mocks.rb | 2 +- lib/rspec/mocks/message_expectation.rb | 4 +- lib/rspec/mocks/method_double.rb | 2 +- lib/rspec/mocks/method_reference.rb | 2 +- lib/rspec/mocks/method_signature_verifier.rb | 202 ------------- lib/rspec/mocks/proxy.rb | 2 +- lib/rspec/mocks/ruby_features.rb | 29 -- .../mocks/verifying_message_expecation.rb | 4 +- lib/rspec/mocks/verifying_proxy.rb | 4 +- .../mocks/method_signature_verifier_spec.rb | 270 ------------------ spec/rspec/mocks/stub_spec.rb | 4 +- spec/rspec/mocks/verifying_double_spec.rb | 2 +- .../verifying_message_expecation_spec.rb | 4 +- spec/spec_helper.rb | 6 +- 14 files changed, 18 insertions(+), 519 deletions(-) delete mode 100644 lib/rspec/mocks/method_signature_verifier.rb delete mode 100644 lib/rspec/mocks/ruby_features.rb delete mode 100644 spec/rspec/mocks/method_signature_verifier_spec.rb diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 70ad5fa90..ca518a0af 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -1,12 +1,12 @@ require 'rspec/support' RSpec::Support.require_rspec_support 'caller_filter' RSpec::Support.require_rspec_support 'warnings' +RSpec::Support.require_rspec_support 'ruby_features' RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f } %w[ instance_method_stasher - ruby_features method_double argument_matchers example_methods diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index edb1b6b70..4047ba9c0 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -541,10 +541,10 @@ def call(*args_to_ignore, &block) @error_generator.raise_missing_block_error @args_to_yield unless block value = nil - block_signature = BlockSignature.new(block) + block_signature = Support::BlockSignature.new(block) @args_to_yield.each do |args| - unless MethodSignatureVerifier.new(block_signature, args).valid? + unless Support::MethodSignatureVerifier.new(block_signature, args).valid? @error_generator.raise_wrong_arity_error(args, block_signature) end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 73e8cc95b..28388eca5 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -216,7 +216,7 @@ def raise_method_not_stubbed_error # that is either the singleton class, or if necessary, a prepended module # of our own. # - if RubyFeatures.module_prepends_supported? + if Support::RubyFeatures.module_prepends_supported? # We subclass `Module` in order to be able to easily detect our prepended module. RSpecPrependedModule = Class.new(Module) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 85021ade3..cfc1f86f5 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -45,7 +45,7 @@ def defined? def with_signature if original = original_method - yield MethodSignature.new(original) + yield Support::MethodSignature.new(original) end end diff --git a/lib/rspec/mocks/method_signature_verifier.rb b/lib/rspec/mocks/method_signature_verifier.rb deleted file mode 100644 index 101db2234..000000000 --- a/lib/rspec/mocks/method_signature_verifier.rb +++ /dev/null @@ -1,202 +0,0 @@ -module RSpec - module Mocks - # Extracts info about the number of arguments and allowed/required - # keyword args of a given method. - # - # @private - class MethodSignature - attr_reader :min_non_kw_args, :max_non_kw_args - - def initialize(method) - @method = method - classify_parameters - end - - def non_kw_args_arity_description - case max_non_kw_args - when min_non_kw_args then min_non_kw_args.to_s - when INFINITY then "#{min_non_kw_args} or more" - else "#{min_non_kw_args} to #{max_non_kw_args}" - end - end - - if RubyFeatures.optional_and_splat_args_supported? - def description - @description ||= begin - parts = [] - - unless non_kw_args_arity_description == "0" - parts << "arity of #{non_kw_args_arity_description}" - end - - if @optional_kw_args.any? - parts << "optional keyword args (#{@optional_kw_args.map(&:inspect).join(", ")})" - end - - if @required_kw_args.any? - parts << "required keyword args (#{@required_kw_args.map(&:inspect).join(", ")})" - end - - if @allows_any_kw_args - parts << "any additional keyword args" - end - - parts.join(" and ") - end - end - - def missing_kw_args_from(given_kw_args) - @required_kw_args - given_kw_args - end - - def invalid_kw_args_from(given_kw_args) - return [] if @allows_any_kw_args - given_kw_args - @allowed_kw_args - end - - def has_kw_args_in?(args) - return false unless Hash === args.last - return false if args.count <= min_non_kw_args - - @allows_any_kw_args || @allowed_kw_args.any? - end - - def classify_parameters - optional_non_kw_args = @min_non_kw_args = 0 - @optional_kw_args, @required_kw_args = [], [] - @allows_any_kw_args = false - - @method.parameters.each do |(type, name)| - case type - # def foo(a:) - when :keyreq then @required_kw_args << name - # def foo(a: 1) - when :key then @optional_kw_args << name - # def foo(**kw_args) - when :keyrest then @allows_any_kw_args = true - # def foo(a) - when :req then @min_non_kw_args += 1 - # def foo(a = 1) - when :opt then optional_non_kw_args += 1 - # def foo(*a) - when :rest then optional_non_kw_args = INFINITY - end - end - - @max_non_kw_args = @min_non_kw_args + optional_non_kw_args - @allowed_kw_args = @required_kw_args + @optional_kw_args - end - else - def description - "arity of #{non_kw_args_arity_description}" - end - - def missing_kw_args_from(given_kw_args) - [] - end - - def invalid_kw_args_from(given_kw_args) - [] - end - - def has_kw_args_in?(args) - false - end - - def classify_parameters - arity = @method.arity - if arity < 0 - # `~` inverts the one's complement and gives us the - # number of required args - @min_non_kw_args = ~arity - @max_non_kw_args = INFINITY - else - @min_non_kw_args = arity - @max_non_kw_args = arity - end - end - end - - INFINITY = 1/0.0 - end - - # Deals with the slightly different semantics of block arguments. - # For methods, arguments are required unless a default value is provided. - # For blocks, arguments are optional, even if no default value is provided. - # - # However, we want to treat block args as required since you virtually always - # want to pass a value for each received argument and our `and_yield` has - # treated block args as required for many years. - # - # @api private - class BlockSignature < MethodSignature - if RubyFeatures.optional_and_splat_args_supported? - def classify_parameters - super - @min_non_kw_args = @max_non_kw_args unless @max_non_kw_args == INFINITY - end - end - end - - # Figures out wheter a given method can accept various arguments. - # Surprisingly non-trivial. - # - # @private - class MethodSignatureVerifier - attr_reader :non_kw_args, :kw_args - - def initialize(signature, args) - @signature = signature - @non_kw_args, @kw_args = split_args(*args) - end - - def valid? - missing_kw_args.empty? && - invalid_kw_args.empty? && - valid_non_kw_args? - end - - def error_message - if missing_kw_args.any? - "Missing required keyword arguments: %s" % [ - missing_kw_args.join(", ") - ] - elsif invalid_kw_args.any? - "Invalid keyword arguments provided: %s" % [ - invalid_kw_args.join(", ") - ] - elsif !valid_non_kw_args? - "Wrong number of arguments. Expected %s, got %s." % [ - @signature.non_kw_args_arity_description, - non_kw_args.length - ] - end - end - - private - - def valid_non_kw_args? - actual = non_kw_args.length - @signature.min_non_kw_args <= actual && actual <= @signature.max_non_kw_args - end - - def missing_kw_args - @signature.missing_kw_args_from(kw_args) - end - - def invalid_kw_args - @signature.invalid_kw_args_from(kw_args) - end - - def split_args(*args) - kw_args = if @signature.has_kw_args_in?(args) - args.pop.keys - else - [] - end - - [args, kw_args] - end - end - end -end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index a4bb66571..2cb88a496 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -194,7 +194,7 @@ def visibility_for(method_name) :public end - if RubyFeatures.module_prepends_supported? + if Support::RubyFeatures.module_prepends_supported? def prepended_modules_of_singleton_class @prepended_modules_of_singleton_class ||= begin singleton_class = @object.singleton_class diff --git a/lib/rspec/mocks/ruby_features.rb b/lib/rspec/mocks/ruby_features.rb deleted file mode 100644 index bd89378b1..000000000 --- a/lib/rspec/mocks/ruby_features.rb +++ /dev/null @@ -1,29 +0,0 @@ -module RSpec - module Mocks - # @api private - # - # Provides query methods for ruby features that differ among - # implementations. - module RubyFeatures - def optional_and_splat_args_supported? - Method.method_defined?(:parameters) - end - module_function :optional_and_splat_args_supported? - - def kw_args_supported? - RUBY_VERSION >= '2.0.0' && RUBY_ENGINE != 'rbx' - end - module_function :kw_args_supported? - - def required_kw_args_supported? - RUBY_VERSION >= '2.1.0' && RUBY_ENGINE != 'rbx' - end - module_function :required_kw_args_supported? - - def module_prepends_supported? - RUBY_VERSION.to_f >= 2.0 - end - module_function :module_prepends_supported? - end - end -end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 7185f536d..0fa2f6cab 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -1,4 +1,4 @@ -RSpec::Support.require_rspec_mocks 'method_signature_verifier' +RSpec::Support.require_rspec_support 'method_signature_verifier' module RSpec module Mocks @@ -45,7 +45,7 @@ def validate_arguments!(actual_args) return if method_reference.nil? method_reference.with_signature do |signature| - verifier = MethodSignatureVerifier.new(signature, actual_args) + verifier = Support::MethodSignatureVerifier.new(signature, actual_args) unless verifier.valid? # Fail fast is required, otherwise the message expecation will fail # as well ("expected method not called") and clobber this one. diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 8150a4f14..b07cc8062 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -127,7 +127,7 @@ def proxy_method_invoked(obj, *args, &block) def validate_arguments!(actual_args) @method_reference.with_signature do |signature| - verifier = MethodSignatureVerifier.new(signature, actual_args) + verifier = Support::MethodSignatureVerifier.new(signature, actual_args) unless verifier.valid? raise ArgumentError, verifier.error_message end @@ -154,7 +154,7 @@ def initialize(object, method_name, proxy) end def with_signature - yield MethodSignature.new(original_method) + yield Support::MethodSignature.new(original_method) end def unimplemented? diff --git a/spec/rspec/mocks/method_signature_verifier_spec.rb b/spec/rspec/mocks/method_signature_verifier_spec.rb deleted file mode 100644 index cc0471f62..000000000 --- a/spec/rspec/mocks/method_signature_verifier_spec.rb +++ /dev/null @@ -1,270 +0,0 @@ -module RSpec - module Mocks - describe MethodSignatureVerifier do - describe '#verify!' do - let(:signature) { MethodSignature.new(test_method) } - - def valid_non_kw_args?(arity) - described_class.new(signature, [nil] * arity).valid? - end - - def valid?(*args) - described_class.new(signature, args).valid? - end - - def error_description - described_class.new(signature, []).error_message[/Expected (.*),/, 1] - end - - def error_for(*args) - described_class.new(signature, args).error_message - end - - def signature_description - signature.description - end - - describe 'with a method with arguments' do - def arity_two(x, y); end - - let(:test_method) { method(:arity_two) } - - it 'covers only the exact arity' do - expect(valid_non_kw_args?(1)).to eq(false) - expect(valid_non_kw_args?(2)).to eq(true) - expect(valid_non_kw_args?(3)).to eq(false) - end - - it 'does not treat a last-arg hash as kw args' do - expect(valid?(1, {})).to eq(true) - end - - it 'describes the arity precisely' do - expect(error_description).to eq("2") - end - - it 'mentions only the arity in the description' do - expect(signature_description).to eq("arity of 2") - end - end - - describe 'a method with splat arguments' do - def arity_splat(_, *); end - - let(:test_method) { method(:arity_splat) } - - it 'covers a range from the lower bound upwards' do - expect(valid_non_kw_args?(0)).to eq(false) - expect(valid_non_kw_args?(1)).to eq(true) - expect(valid_non_kw_args?(2)).to eq(true) - expect(valid_non_kw_args?(3)).to eq(true) - end - - it 'describes the arity with no upper bound' do - expect(error_description).to eq("1 or more") - end - - it 'mentions only the arity in the description' do - expect(signature_description).to eq("arity of 1 or more") - end - end - - describe 'a method with optional arguments' do - def arity_optional(x, y, z = 1); end - - let(:test_method) { method(:arity_optional) } - - it 'covers a range from min to max possible arguments' do - expect(valid_non_kw_args?(1)).to eq(false) - expect(valid_non_kw_args?(2)).to eq(true) - expect(valid_non_kw_args?(3)).to eq(true) - - if optional_and_splat_args_supported? - expect(valid_non_kw_args?(4)).to eq(false) - else - expect(valid_non_kw_args?(4)).to eq(true) - end - end - - if optional_and_splat_args_supported? - it 'describes the arity as a range' do - expect(error_description).to eq("2 to 3") - end - else - it 'describes the arity with no upper bound' do - expect(error_description).to eq("2 or more") - end - end - end - - if kw_args_supported? - describe 'a method with optional keyword arguments' do - eval <<-RUBY - def arity_kw(x, y:1, z:2); end - RUBY - - let(:test_method) { method(:arity_kw) } - - it 'does not require any of the arguments' do - expect(valid?(nil)).to eq(true) - expect(valid?(nil, nil)).to eq(false) - end - - it 'does not allow an invalid keyword arguments' do - expect(valid?(nil, :a => 1)).to eq(false) - end - - it 'mentions the invalid keyword args in the error' do - expect(error_for(nil, :a => 0, :b => 1)).to \ - eq("Invalid keyword arguments provided: a, b") - end - - it 'describes invalid arity precisely' do - expect(error_for()).to \ - eq("Wrong number of arguments. Expected 1, got 0.") - end - - it 'does not blow up when given a BasicObject as the last arg' do - expect(valid?(BasicObject.new)).to eq(true) - end - - it 'does not mutate the provided args array' do - args = [nil, { :y => 1 }] - described_class.new(signature, args).valid? - expect(args).to eq([nil, { :y => 1 }]) - end - - it 'mentions the arity and optional kw args in the description' do - expect(signature_description).to eq("arity of 1 and optional keyword args (:y, :z)") - end - end - end - - if required_kw_args_supported? - describe 'a method with required keyword arguments' do - eval <<-RUBY - def arity_required_kw(x, y:, z:, a: 'default'); end - RUBY - - let(:test_method) { method(:arity_required_kw) } - - it 'returns false unless all required keywords args are present' do - expect(valid?(nil, :a => 0, :y => 1, :z => 2)).to eq(true) - expect(valid?(nil, :a => 0, :y => 1)).to eq(false) - expect(valid?(nil, nil, :a => 0, :y => 1, :z => 2)).to eq(false) - expect(valid?(nil, nil)).to eq(false) - end - - it 'mentions the missing required keyword args in the error' do - expect(error_for(nil, :a => 0)).to \ - eq("Missing required keyword arguments: y, z") - end - - it 'is described precisely when arity is wrong' do - expect(error_for(nil, nil, :z => 0, :y => 1)).to \ - eq("Wrong number of arguments. Expected 1, got 2.") - end - - it 'mentions the arity, optional kw args and required kw args in the description' do - expect(signature_description).to \ - eq("arity of 1 and optional keyword args (:a) and required keyword args (:y, :z)") - end - end - - describe 'a method with required keyword arguments and a splat' do - eval <<-RUBY - def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end - RUBY - - let(:test_method) { method(:arity_required_kw_splat) } - - it 'returns false unless all required keywords args are present' do - expect(valid?(nil, :a => 0, :y => 1, :z => 2)).to eq(true) - expect(valid?(nil, :a => 0, :y => 1)).to eq(false) - expect(valid?(nil, nil, :a => 0, :y => 1, :z => 2)).to eq(true) - expect(valid?(nil, nil, nil)).to eq(false) - expect(valid?).to eq(false) - end - - it 'mentions missing required keyword args in the error' do - expect(error_for(nil, :y => 1)).to \ - eq("Missing required keyword arguments: z") - end - - it 'mentions the arity, optional kw args and required kw args in the description' do - expect(signature_description).to \ - eq("arity of 1 or more and optional keyword args (:a) and required keyword args (:y, :z)") - end - end - - describe 'a method with required keyword arguments and a keyword arg splat' do - eval <<-RUBY - def arity_kw_arg_splat(x:, **rest); end - RUBY - - let(:test_method) { method(:arity_kw_arg_splat) } - - it 'allows extra undeclared keyword args' do - expect(valid?(:x => 1)).to eq(true) - expect(valid?(:x => 1, :y => 2)).to eq(true) - end - - it 'mentions missing required keyword args in the error' do - expect(error_for(:y => 1)).to \ - eq("Missing required keyword arguments: x") - end - - it 'mentions the required kw args and keyword splat in the description' do - expect(signature_description).to \ - eq("required keyword args (:x) and any additional keyword args") - end - end - - describe 'a method with a required arg and a keyword arg splat' do - eval <<-RUBY - def arity_kw_arg_splat(x, **rest); end - RUBY - - let(:test_method) { method(:arity_kw_arg_splat) } - - it 'allows a single arg and any number of keyword args' do - expect(valid?(nil)).to eq(true) - expect(valid?(nil, :x => 1)).to eq(true) - expect(valid?(nil, :x => 1, :y => 2)).to eq(true) - expect(valid?(:x => 1)).to eq(true) - - expect(valid?).to eq(false) - expect(valid?(nil, nil)).to eq(false) - expect(valid?(nil, nil, :x => 1)).to eq(false) - end - - it 'describes the arity precisely' do - expect(error_for()).to \ - eq("Wrong number of arguments. Expected 1, got 0.") - end - - it 'mentions the required kw args and keyword splat in the description' do - expect(signature_description).to \ - eq("arity of 1 and any additional keyword args") - end - end - end - - describe 'a method with a block' do - def arity_block(_, &block); end - - let(:test_method) { method(:arity_block) } - - it 'does not count the block as a parameter' do - expect(valid_non_kw_args?(1)).to eq(true) - expect(valid_non_kw_args?(2)).to eq(false) - end - - it 'describes the arity precisely' do - expect(error_description).to eq("1") - end - end - end - end - end -end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 4e646fbdf..f5128693a 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -74,7 +74,7 @@ def existing_private_instance_method expect(@instance.msg2).to eq(2) end - context "stubbing with prepend", :if => RubyFeatures.module_prepends_supported? do + context "stubbing with prepend", :if => Support::RubyFeatures.module_prepends_supported? do module ToBePrepended def value "#{super}_prepended".to_sym @@ -251,7 +251,7 @@ class << self; public :hello; end; expect(mod.hello).to eq(:hello) end - if RubyFeatures.module_prepends_supported? + if Support::RubyFeatures.module_prepends_supported? context "with a prepended module (ruby 2.0.0+)" do module ToBePrepended def existing_method diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index 2ed48248d..eea2ebbef 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -1,5 +1,5 @@ class LoadedClass - extend RSpec::Mocks::RubyFeatures + extend RSpec::Support::RubyFeatures M = :m N = :n diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index b060e87e0..9d1fea555 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -22,7 +22,7 @@ module Mocks args = ["abc123", "xyz987"] subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_invalid_arguments_error). - with(instance_of(MethodSignatureVerifier)) + with(instance_of(Support::MethodSignatureVerifier)) subject.with(*args) end @@ -48,7 +48,7 @@ module Mocks it 'matches arity to 0' do subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_invalid_arguments_error). - with(instance_of(MethodSignatureVerifier)) + with(instance_of(Support::MethodSignatureVerifier)) subject.with(no_args) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8bfd84438..dbdc31978 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,5 @@ require 'rspec/support/spec' -require 'rspec/mocks/ruby_features' +require 'rspec/support/ruby_features' RSpec::Support::Spec.setup_simplecov do minimum_coverage 95 @@ -76,8 +76,8 @@ def prevents(&block) config.include VerifyAndResetHelpers config.include VerificationHelpers - config.extend RSpec::Mocks::RubyFeatures - config.include RSpec::Mocks::RubyFeatures + config.extend RSpec::Support::RubyFeatures + config.include RSpec::Support::RubyFeatures end shared_context "with syntax" do |syntax| From 754cb3f396ea1af76d26ab3ba8b906943e68de31 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 18 Mar 2014 22:35:41 +1100 Subject: [PATCH 0563/1179] primitive fix for inspect accessing unexpected things --- lib/rspec/mocks/any_instance/recorder.rb | 4 +++- spec/rspec/mocks/any_instance_spec.rb | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index e794f8168..2ee6a1933 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -227,9 +227,11 @@ def mark_invoked!(method_name) @klass.__send__(:define_method, method_name) do |*args, &blk| klass = ::RSpec::Support.method_handle_for(self, method_name).owner invoked_instance = ::RSpec::Mocks.space.any_instance_recorder_for(klass).instance_that_received(method_name) - raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{self.inspect} but has already been received by #{invoked_instance}" + inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>" + raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{inspect} but has already been received by #{invoked_instance}" end end + end end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index a9aeb4c09..b45cfe979 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -438,7 +438,7 @@ class RSpec::SampleRspecTestClass;end instance_one.foo instance_two.foo - end.to raise_error(RSpec::Mocks::MockExpectationError, "The message 'foo' was received by #{instance_two.inspect} but has already been received by #{instance_one.inspect}") + end.to raise_error(RSpec::Mocks::MockExpectationError, /The message 'foo' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end @@ -507,7 +507,7 @@ class RSpec::SampleRspecTestClass;end instance_one.existing_method instance_two.existing_method - end.to raise_error(RSpec::Mocks::MockExpectationError, "The message 'existing_method' was received by #{instance_two.inspect} but has already been received by #{instance_one.inspect}") + end.to raise_error(RSpec::Mocks::MockExpectationError, /The message 'existing_method' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end end @@ -984,7 +984,7 @@ def dup(funky_option) expect_any_instance_of(klass).to receive(:existing_method) instance_one.existing_method instance_two.existing_method - end.to raise_error(RSpec::Mocks::MockExpectationError, "The message 'existing_method' was received by #{instance_two.inspect} but has already been received by #{instance_one.inspect}") + end.to raise_error(RSpec::Mocks::MockExpectationError, /The message 'existing_method' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end From 38098abd5a99c02bdb984da9188fe675660d9660 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 19 Mar 2014 08:39:04 +1100 Subject: [PATCH 0564/1179] spec demonstrating issue --- spec/rspec/mocks/any_instance_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index b45cfe979..7bb7128db 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -385,6 +385,21 @@ class RSpec::SampleRspecTestClass;end let(:foo_expectation_error_message) { 'Exactly one instance should have received the following message(s) but didn\'t: foo' } let(:existing_method_expectation_error_message) { 'Exactly one instance should have received the following message(s) but didn\'t: existing_method' } + it "handles inspect accessing expected methods" do + klass.class_eval do + def inspect + "The contents of output: #{stdout}" + end + end + + expect_any_instance_of(klass).to receive(:stdout).at_least(:twice) + expect do + klass.new.stdout + klass.new.stdout + end.to raise_error /The message 'stdout' was received by/ + reset_all + end + context "with an expectation is set on a method which does not exist" do it "returns the expected value" do expect_any_instance_of(klass).to receive(:foo).and_return(1) From 4ed12e7f481a2127b9bddb318131b688247d77bc Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 19 Mar 2014 09:08:30 +1100 Subject: [PATCH 0565/1179] changelog for #630 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index eefff6ab6..94b6b45e5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -29,6 +29,8 @@ Bug Fixes: their singleton class prepended to. (Jon Rowe) * Fix an issue with 1.8.7 not running implementation blocks on partial doubles. (Maurício Linhares) +* Prevent `StackLevelTooDeep` errors when stubbing an `any_instance` method that's + accessed in `inspect` by providing our own inspect output. (Jon Rowe) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) From 907625157bc16e3ea16861935a47b94336a28629 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Sun, 16 Mar 2014 17:00:20 -0400 Subject: [PATCH 0566/1179] Document stub behavior on `clone` and `dup`. For more details on the inner workings on `clone` vs `dup` see: http://stackoverflow.com/a/10183477/29262 `dup` does not copy object state. Stubs are viewed as part of object state, so they are not transfered. However, `clone` should copy an object's state; thus the stubs should also be copied. --- spec/rspec/mocks/partial_double_spec.rb | 15 +++++++++++++++ spec/rspec/mocks/stub_spec.rb | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 96fb45590..0fa18389f 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -100,6 +100,21 @@ def self.inspect klass.bar(2) }.to raise_error(RSpec::Mocks::MockExpectationError, /MyClass/) end + + it "shares message expectations with clone" do + expect(object).to receive(:foobar) + twin = object.clone + twin.foobar + expect{ verify twin }.not_to raise_error + expect{ verify object }.not_to raise_error + end + + it "clears message expectations when `dup`ed" do + expect(object).to receive(:foobar) + duplicate = object.dup + expect{ duplicate.foobar }.to raise_error(NoMethodError, /foobar/) + expect{ verify object }.to raise_error(RSpec::Mocks::MockExpectationError, /foobar/) + end end describe "Using a partial mock on a proxy object", :if => defined?(::BasicObject) do diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index f5128693a..11b36080d 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -74,6 +74,16 @@ def existing_private_instance_method expect(@instance.msg2).to eq(2) end + it "is retained when stubbed object is `clone`d" do + allow(@stub).to receive(:foobar).and_return(1) + expect(@stub.clone.foobar).to eq(1) + end + + it "is cleared when stubbed object when `dup`ed" do + allow(@stub).to receive(:foobar).and_return(1) + expect{ @stub.dup.foobar }.to raise_error NoMethodError, /foobar/ + end + context "stubbing with prepend", :if => Support::RubyFeatures.module_prepends_supported? do module ToBePrepended def value From 3c2f5e5154d1c1a789a888f905b365a2ff10632e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20F=C3=B6hring?= Date: Wed, 19 Mar 2014 16:56:19 +0100 Subject: [PATCH 0567/1179] Add docs badge to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c09a4620e..14e164685 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) +# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) [![Inline docs](http://inch-pages.github.io/github/rspec/rspec-mocks.png)](http://inch-pages.github.io/github/rspec/rspec-mocks) rspec-mocks is a test-double framework for rspec with support for method stubs, fakes, and message expectations on generated test-doubles and real objects From eaaad7c2edae6253ccd874436e321952f4a81b91 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Mar 2014 00:51:25 -0700 Subject: [PATCH 0568/1179] Standardize on `shared_examples`. `shared_examples_for` is being removed. --- spec/rspec/mocks/configuration_spec.rb | 2 +- spec/rspec/mocks/matchers/receive_messages_spec.rb | 4 ++-- spec/rspec/mocks/matchers/receive_spec.rb | 14 +++++++------- spec/rspec/mocks/mutate_const_spec.rb | 8 ++++---- spec/rspec/mocks/serialization_spec.rb | 2 +- spec/rspec/mocks/verifying_double_spec.rb | 4 ++-- spec/support/before_all_shared_example_group.rb | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 53772a5cb..e26b3ccd2 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -19,7 +19,7 @@ def instance_methods_of(mod) expect(instance_methods_of(mod_2)).to include(:stub, :should_receive) end - shared_examples_for "configuring the syntax" do + shared_examples "configuring the syntax" do def sandboxed orig_syntax = RSpec::Mocks.configuration.syntax yield diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 7cfe176a8..28484cc52 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - shared_examples_for "complains when given blocks" do + shared_examples "complains when given blocks" do it "complains if a { } block is given" do expect { target.to receive_messages(:a => 1) { "implementation" } @@ -16,7 +16,7 @@ module Mocks end end - shared_examples_for "handles partially mocked objects correctly" do + shared_examples "handles partially mocked objects correctly" do let(:obj) { Struct.new(:a).new('original') } it "resets partially mocked objects correctly" do diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 798cf8dd9..6cc552d10 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -29,7 +29,7 @@ module Mocks end end - shared_examples_for "a receive matcher" do |*options| + shared_examples "a receive matcher" do |*options| it 'allows the caller to configure how the subject responds' do wrapped.to receive(:foo).and_return(5) expect(receiver.foo).to eq(5) @@ -107,7 +107,7 @@ module Mocks end end - shared_examples_for "an expect syntax allowance" do |*options| + shared_examples "an expect syntax allowance" do |*options| it_behaves_like "a receive matcher", *options it 'does not expect the message to be received' do @@ -116,7 +116,7 @@ module Mocks end end - shared_examples_for "an expect syntax negative allowance" do + shared_examples "an expect syntax negative allowance" do it 'is disabled since this expression is confusing' do expect { wrapped.not_to receive(:foo) @@ -128,7 +128,7 @@ module Mocks end end - shared_examples_for "an expect syntax expectation" do |*options| + shared_examples "an expect syntax expectation" do |*options| it_behaves_like "a receive matcher", *options it 'sets up a message expectation that passes if the message is received' do @@ -156,7 +156,7 @@ module Mocks end end - shared_examples_for "an expect syntax negative expectation" do + shared_examples "an expect syntax negative expectation" do it 'sets up a negaive message expectation that passes if the message is not received' do wrapped.not_to receive(:foo) verify_all @@ -202,7 +202,7 @@ def receiver.method_missing(*a); end # a poor man's stub... end end - shared_examples_for "resets partial mocks cleanly" do + shared_examples "resets partial mocks cleanly" do let(:klass) { Struct.new(:foo) } let(:object) { klass.new :bar } @@ -229,7 +229,7 @@ def receiver.method_missing(*a); end # a poor man's stub... end end - shared_examples_for "resets partial mocks of any instance cleanly" do + shared_examples "resets partial mocks of any instance cleanly" do let(:klass) { Struct.new(:foo) } let(:object) { klass.new :bar } diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 8db61a898..9bbb51978 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -44,7 +44,7 @@ def reset_rspec_mocks end end - shared_examples_for "loaded constant stubbing" do |const_name| + shared_examples "loaded constant stubbing" do |const_name| include_context "constant example methods", const_name let!(:original_const_value) { const } @@ -74,7 +74,7 @@ def change_const_value_to(value) end end - shared_examples_for "loaded constant hiding" do |const_name| + shared_examples "loaded constant hiding" do |const_name| before do expect(recursive_const_defined?(const_name)).to be_truthy end @@ -95,7 +95,7 @@ def change_const_value_to(value) end end - shared_examples_for "unloaded constant stubbing" do |const_name| + shared_examples "unloaded constant stubbing" do |const_name| include_context "constant example methods", const_name before do @@ -124,7 +124,7 @@ def change_const_value_to(value) end end - shared_examples_for "unloaded constant hiding" do |const_name| + shared_examples "unloaded constant hiding" do |const_name| include_context "constant example methods", const_name before do diff --git a/spec/rspec/mocks/serialization_spec.rb b/spec/rspec/mocks/serialization_spec.rb index e22a867a4..a3c60f241 100644 --- a/spec/rspec/mocks/serialization_spec.rb +++ b/spec/rspec/mocks/serialization_spec.rb @@ -39,7 +39,7 @@ def set_stub allow(serializable_object).to receive_messages(:bazz => 5) end - shared_examples_for 'normal YAML serialization' do + shared_examples 'normal YAML serialization' do it 'serializes to yaml the same with and without stubbing, using #to_yaml' do expect { set_stub }.to_not change { serializable_object.to_yaml } end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index eea2ebbef..ac538b2d6 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -155,7 +155,7 @@ module Mocks end describe "method visibility" do - shared_examples_for "preserves method visibility" do |visibility| + shared_examples "preserves method visibility" do |visibility| method_name = :"defined_#{visibility}_method" it "can allow a #{visibility} instance method" do @@ -375,7 +375,7 @@ def use; end end describe "method visibility" do - shared_examples_for "preserves method visibility" do |visibility| + shared_examples "preserves method visibility" do |visibility| method_name = :"defined_#{visibility}_class_method" it "can allow a #{visibility} instance method" do diff --git a/spec/support/before_all_shared_example_group.rb b/spec/support/before_all_shared_example_group.rb index e9b6a8f85..e9dc3226c 100644 --- a/spec/support/before_all_shared_example_group.rb +++ b/spec/support/before_all_shared_example_group.rb @@ -1,4 +1,4 @@ -shared_examples_for "fails in a before(:all) block" do +shared_examples "fails in a before(:all) block" do the_error = nil before(:all) do begin From 5f881b96cf5f449ec18637c4a623db661bcaab97 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Mar 2014 00:52:27 -0700 Subject: [PATCH 0569/1179] Address ruby warnings. --- spec/rspec/mocks/any_instance_spec.rb | 2 +- spec/rspec/mocks/argument_matchers_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 7bb7128db..27f0d31e0 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -396,7 +396,7 @@ def inspect expect do klass.new.stdout klass.new.stdout - end.to raise_error /The message 'stdout' was received by/ + end.to raise_error(/The message 'stdout' was received by/) reset_all end diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index 8cb68c23b..75448de0f 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -261,14 +261,14 @@ module Mocks expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") expect do a_double.random_call(:a => "b", :c => "e") - end.to fail_matching /expected: \({(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")}\)/ + end.to fail_matching(/expected: \({(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")}\)/) end it "fails for a hash w/ wrong keys", :reset => true do expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") expect do a_double.random_call("a" => "b", "c" => "d") - end.to fail_matching /expected: \({(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")}\)/ + end.to fail_matching(/expected: \({(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")}\)/) end it "matches a class against itself" do From 18516784b92c17dbdd40089d07046d0b60b242fe Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 22 Mar 2014 00:54:29 -0700 Subject: [PATCH 0570/1179] Use modern scope names for hooks. --- README.md | 6 +++--- features/Scope.md | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 14e164685..5250571f1 100644 --- a/README.md +++ b/README.md @@ -324,11 +324,11 @@ See the [mutating constants README](https://github.com/rspec/rspec-mocks/blob/master/features/mutating_constants/README.md) for info on this feature. -## Use `before(:each)`, not `before(:all)` +## Use `before(:example)`, not `before(:context)` -Stubs in `before(:all)` are not supported. The reason is that all stubs and mocks get cleared out after each example, so any stub that is set in `before(:all)` would work in the first example that happens to run in that group, but not for any others. +Stubs in `before(:context)` are not supported. The reason is that all stubs and mocks get cleared out after each example, so any stub that is set in `before(:context)` would work in the first example that happens to run in that group, but not for any others. -Instead of `before(:all)`, use `before(:each)`. +Instead of `before(:context)`, use `before(:example)`. ## Further Reading diff --git a/features/Scope.md b/features/Scope.md index c9d0a08c0..8d11d29ef 100644 --- a/features/Scope.md +++ b/features/Scope.md @@ -2,16 +2,16 @@ Doubles, stubs, and message expectations are all cleaned out after each example. This ensures that each example can be run in isolation, and in any order. -### `before(:each)` +### `before(:example)` It is perfectly fine to set up doubles, stubs, and message expectations in -a `before(:each)` hook, as that hook is executed in the scope of the example: +a `before(:example)` hook, as that hook is executed in the scope of the example: - before(:each) do + before(:example) do @account = double('account') end -### Do not create doubles, stubs, or message expectations in `before(:all)` +### Do not create doubles, stubs, or message expectations in `before(:context)` If you do, they'll get cleaned out after the first example, and you will be very confused as to what's going on in the second example. From 9674568ba2489b0cbbfe335df087a37578c4d74b Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 22 Mar 2014 10:36:59 -0700 Subject: [PATCH 0571/1179] Make RSpec::Mocks::Space more thread-safe. It used to be possible for different threads to receive different proxy objects, potentially a source of bugs such as #380. This is non-trivial to write a spec for, but you can trivially demonstrate the problem by adding a `sleep` into the fetch blocks, and then running: o = Object.new t = Thread.new do RSpec::Mocks.space.proxy_for(o) end b = RSpec::Mocks.space.proxy_for(o) a = t.value expect(a).to eq(b) --- benchmarks/thread_safety.rb | 24 ++++++++++++++++++++++++ lib/rspec/mocks/space.rb | 31 ++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 benchmarks/thread_safety.rb diff --git a/benchmarks/thread_safety.rb b/benchmarks/thread_safety.rb new file mode 100644 index 000000000..6ab24274d --- /dev/null +++ b/benchmarks/thread_safety.rb @@ -0,0 +1,24 @@ +$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) + +require 'benchmark' +require 'rspec/mocks' + +Benchmark.bm do |bm| + bm.report("fetching a proxy") do + RSpec::Mocks.with_temporary_scope do + o = Object.new + 100000.times { + RSpec::Mocks.space.proxy_for(o) + } + end + end +end + +# Without synchronize (not thread-safe): +# +# user system total real +# fetching a proxy 0.120000 0.000000 0.120000 ( 0.141333) +# +# With synchronize (thread-safe): +# user system total real +# fetching a proxy 0.180000 0.000000 0.180000 ( 0.189553) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index a6e45e611..3a94bf373 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -48,6 +48,8 @@ def initialize @any_instance_recorders = {} @constant_mutators = [] @expectation_ordering = OrderGroup.new + @proxy_mutex = new_mutex + @any_instance_mutex = new_mutex end def new_scope @@ -75,9 +77,11 @@ def constant_mutator_for(name) end def any_instance_recorder_for(klass) - id = klass.__id__ - any_instance_recorders.fetch(id) do - any_instance_recorder_not_found_for(id, klass) + any_instance_mutex.synchronize do + id = klass.__id__ + any_instance_recorders.fetch(id) do + any_instance_recorder_not_found_for(id, klass) + end end end @@ -86,8 +90,10 @@ def proxies_of(klass) end def proxy_for(object) - id = id_for(object) - proxies.fetch(id) { proxy_not_found_for(id, object) } + proxy_mutex.synchronize do + id = id_for(object) + proxies.fetch(id) { proxy_not_found_for(id, object) } + end end alias ensure_registered proxy_for @@ -98,6 +104,21 @@ def registered?(object) private + attr_reader :proxy_mutex, :any_instance_mutex + + # We don't want to depend on the stdlib ourselves, but if the user is + # using threads then a Mutex will be available to us. If not, we don't + # need to synchronize anyway. + def new_mutex + (defined?(::Mutex) ? ::Mutex : FakeMutex).new + end + + class FakeMutex + def synchronize + yield + end + end + def proxy_not_found_for(id, object) proxies[id] = case object when NilClass then ProxyForNil.new(@expectation_ordering) From d680c26b7966d37b18325a70a1b0173c90d85e16 Mon Sep 17 00:00:00 2001 From: Thomas Holmes Date: Sat, 15 Feb 2014 20:42:00 -0500 Subject: [PATCH 0572/1179] Add section to README for any instance features. rspec/rspec-mocks#578 --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index 5250571f1..ce477c27c 100644 --- a/README.md +++ b/README.md @@ -330,6 +330,37 @@ Stubs in `before(:context)` are not supported. The reason is that all stubs and Instead of `before(:context)`, use `before(:example)`. +## Settings mocks or stubs on any instance of a class + +rspec-mocks provides two methods, `allow_any_instance_of` and +`expect_any_instance_of`, that will allow you to stub or mock any instance +of a class. They are used in place for `allow` or `expect`: + +```ruby +allow_any_instance_of(Widget).to receive(:name).and_return("Wibble") +expect_any_instance_of(Widget).to receive(:name).and_return("Wobble") +``` + +These methods add the appropriate stub or expectation to all instances of +`Widget`. + +This feature is sometimes useful when working with legacy code, though in +general we discourage its use for a number of reasons: + +* The `rspec-mocks` API is designed for individual object instances, but this + feature operates on entire classes of objects. As a result there are some + sematically confusing edge cases. For example in + `expect_any_instance_of(Widget).to receive(:name).twice` it isn't clear + whether each specific instance is allowed to receive `name` twice, or if two + receives total are allowed. (It's the former.) +* Using this feature is often a design smell. It may be + that your test is trying to do too much or that the object under test is too + complex. +* It is the most complicated feature of `rspec-mocks`, and has historically + received the most bug reports. (None of the core team actively use it, + which doesn't help.) + + ## Further Reading There are many different viewpoints about the meaning of mocks and stubs. If From b3f778f42a9ae11747695e2335fb656eb98a45f8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 23 Mar 2014 23:47:24 -0700 Subject: [PATCH 0573/1179] Remove unused attribute. --- lib/rspec/mocks/space.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 3a94bf373..38240a989 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -41,7 +41,7 @@ def raise_lifecycle_message # @private class Space - attr_reader :proxies, :any_instance_recorders, :expectation_ordering + attr_reader :proxies, :any_instance_recorders def initialize @proxies = {} From 4b6f65e326dbd92d784c22d7c55cd73625393d70 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 23 Mar 2014 23:48:01 -0700 Subject: [PATCH 0574/1179] Address ruby warning. /Users/myron/code/rspec-dev/repos/rspec-mocks/lib/rspec/mocks/space.rb:107: warning: private attribute? --- lib/rspec/mocks/space.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 38240a989..62bd90cc7 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -41,7 +41,7 @@ def raise_lifecycle_message # @private class Space - attr_reader :proxies, :any_instance_recorders + attr_reader :proxies, :any_instance_recorders, :proxy_mutex, :any_instance_mutex def initialize @proxies = {} @@ -104,8 +104,6 @@ def registered?(object) private - attr_reader :proxy_mutex, :any_instance_mutex - # We don't want to depend on the stdlib ourselves, but if the user is # using threads then a Mutex will be available to us. If not, we don't # need to synchronize anyway. From 9613f2fd0bcb3285b12aa4469b7971fca8b4ab3a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 23 Mar 2014 23:54:41 -0700 Subject: [PATCH 0575/1179] Make FakeMutex a singleton object. It's stateless so there's no need to create new instances; it's just a waste of memory. --- lib/rspec/mocks/space.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 62bd90cc7..f77baca8b 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -108,11 +108,12 @@ def registered?(object) # using threads then a Mutex will be available to us. If not, we don't # need to synchronize anyway. def new_mutex - (defined?(::Mutex) ? ::Mutex : FakeMutex).new + defined?(::Mutex) ? ::Mutex.new : FakeMutex end - class FakeMutex - def synchronize + # @private + module FakeMutex + def self.synchronize yield end end From 65a88f15f681f71207f188ae45e10ebd6c0dd186 Mon Sep 17 00:00:00 2001 From: Oren Dobzinski Date: Wed, 26 Mar 2014 08:56:31 -0400 Subject: [PATCH 0576/1179] [Fixes #640] Support verifying private methods for any_instance --- lib/rspec/mocks/any_instance/recorder.rb | 2 +- spec/rspec/mocks/partial_double_spec.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 2ee6a1933..1a42a7811 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -209,7 +209,7 @@ def public_protected_or_private_method_defined?(method_name) def observe!(method_name) if RSpec::Mocks.configuration.verify_partial_doubles? - raise MockExpectationError unless @klass.method_defined?(method_name) + raise MockExpectationError unless public_protected_or_private_method_defined?(method_name) end stop_observing!(method_name) if already_observing?(method_name) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 0fa18389f..d6cfd3c75 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -303,6 +303,11 @@ def defined_private_method object.implemented end + it 'allows private methods to be expected on any_instance' do + expect_any_instance_of(klass).to receive(:defined_private_method).and_call_original + object.send(:defined_private_method) + end + it 'does not allow a non-existing method to be called on any_instance' do prevents { expect_any_instance_of(klass).to receive(:unimplemented) } end From 8eb3f9e1da2ccd7020fc9e1fa03f15123ad9e960 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 26 Mar 2014 07:50:03 -0700 Subject: [PATCH 0577/1179] Add changelog entry for #641. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 94b6b45e5..461796ebe 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,6 +31,8 @@ Bug Fixes: (Maurício Linhares) * Prevent `StackLevelTooDeep` errors when stubbing an `any_instance` method that's accessed in `inspect` by providing our own inspect output. (Jon Rowe) +* Fix bug in `any_instance` logic that dd not allow you to mock or stub + private methods if `verify_partial_doubles` was configured. (Oren Dobzinski) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) From 6cef31358a9f9f435c7ced567e1c2e19e5327ce1 Mon Sep 17 00:00:00 2001 From: Oren Dobzinski Date: Wed, 26 Mar 2014 10:54:42 -0400 Subject: [PATCH 0578/1179] fix a small typo in changelog --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 461796ebe..42dfd5f88 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,7 +31,7 @@ Bug Fixes: (Maurício Linhares) * Prevent `StackLevelTooDeep` errors when stubbing an `any_instance` method that's accessed in `inspect` by providing our own inspect output. (Jon Rowe) -* Fix bug in `any_instance` logic that dd not allow you to mock or stub +* Fix bug in `any_instance` logic that did not allow you to mock or stub private methods if `verify_partial_doubles` was configured. (Oren Dobzinski) ### 3.0.0.beta2 / 2014-02-17 From eb9df7c6580e8e2893462bb97ae21f20bb19f18f Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 28 Mar 2014 22:28:39 +1100 Subject: [PATCH 0579/1179] remove color_enabled in favour of color --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dbdc31978..dd8ea326b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -52,7 +52,7 @@ def prevents(&block) RSpec.configure do |config| config.mock_with :rspec - config.color_enabled = true + config.color = true config.order = :random config.expect_with :rspec do |expectations| From 9e89d7b65651ff8d2aa18da89bf38b9d57427d4e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 25 Mar 2014 22:22:16 +1100 Subject: [PATCH 0580/1179] match mocks to expectations/core and not exclude yard --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index bf1f9c41c..b44027b3f 100644 --- a/Gemfile +++ b/Gemfile @@ -11,9 +11,10 @@ gemspec end end +gem 'yard', '~> 0.8.7', :require => false + ### deps for rdoc.info group :documentation do - gem 'yard', '0.8.0', :require => false gem 'redcarpet', '2.1.1' unless RUBY_PLATFORM == 'java' gem 'github-markup', '0.7.2' end From 420df1c5177b13910ae31148129fd4cfff0d6f8e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 26 Mar 2014 08:10:19 +1100 Subject: [PATCH 0581/1179] `@api private` had no effect here so lets make them `@private` --- lib/rspec/mocks/mutate_const.rb | 2 +- lib/rspec/mocks/verifying_message_expecation.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index 1bed7b156..b5c640202 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -97,7 +97,7 @@ def initialize(name) # nil if the constant was not previously defined. attr_accessor :original_value - # @api private + # @private attr_writer :previously_defined, :stubbed, :hidden # @return [Boolean] Whether or not the constant was defined diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 0fa2f6cab..93df99d10 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -23,6 +23,7 @@ def initialize(*args) super end + # @private def with(*args, &block) unless ArgumentMatchers::AnyArgsMatcher === args.first expected_args = if ArgumentMatchers::NoArgsMatcher === args.first From 74ff41fd63a5d38687e24c35c05f7b212fc43e2e Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 29 Mar 2014 10:42:26 -0700 Subject: [PATCH 0582/1179] Provide a failure message when trying to observe an unimplemented method on any instance. --- Changelog.md | 2 ++ lib/rspec/mocks/any_instance/recorder.rb | 5 ++++- spec/rspec/mocks/partial_double_spec.rb | 8 ++++++-- spec/spec_helper.rb | 4 ++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 42dfd5f88..6a74198ef 100644 --- a/Changelog.md +++ b/Changelog.md @@ -33,6 +33,8 @@ Bug Fixes: accessed in `inspect` by providing our own inspect output. (Jon Rowe) * Fix bug in `any_instance` logic that did not allow you to mock or stub private methods if `verify_partial_doubles` was configured. (Oren Dobzinski) +* Include useful error message when trying to observe an unimplemented method + on an any instance. (Xavier Shay) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 1a42a7811..b43ea2242 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -209,7 +209,10 @@ def public_protected_or_private_method_defined?(method_name) def observe!(method_name) if RSpec::Mocks.configuration.verify_partial_doubles? - raise MockExpectationError unless public_protected_or_private_method_defined?(method_name) + unless public_protected_or_private_method_defined?(method_name) + raise MockExpectationError, + "#{@klass} does not implement ##{method_name}" + end end stop_observing!(method_name) if already_observing?(method_name) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index d6cfd3c75..4222ccd6d 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -309,14 +309,18 @@ def defined_private_method end it 'does not allow a non-existing method to be called on any_instance' do - prevents { expect_any_instance_of(klass).to receive(:unimplemented) } + prevents(/does not implement/) { + expect_any_instance_of(klass).to receive(:unimplemented) + } end it 'does not allow missing methods to be called on any_instance' do # This is potentially surprising behaviour, but there is no way for us # to know that this method is valid since we only have class and not an # instance. - prevents { expect_any_instance_of(klass).to receive(:dynamic_method) } + prevents(/does not implement/) { + expect_any_instance_of(klass).to receive(:dynamic_method) + } end it 'verifies arity range when receiving a message' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dd8ea326b..6fc3917ca 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -42,9 +42,9 @@ def reset_all end module VerificationHelpers - def prevents(&block) + def prevents(msg = //, &block) expect(&block).to \ - raise_error(RSpec::Mocks::MockExpectationError) + raise_error(RSpec::Mocks::MockExpectationError, msg) end end From 904afeef0e618a76409a6f9d65a1aad71aff790b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 29 Mar 2014 16:39:17 -0700 Subject: [PATCH 0583/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 2 +- script/clone_all_rspec_repos | 3 ++- script/functions.sh | 12 +++++++++--- script/run_build | 5 ++++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5caedd931..8aa3f3291 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-03-14T11:33:06+13:00 from the rspec-dev repo. +# This file was generated on 2014-03-29T16:39:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: "script/clone_all_rspec_repos" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 601f322ba..2d88979f8 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-03-14T11:33:06+13:00 from the rspec-dev repo. +# This file was generated on 2014-03-29T16:39:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -12,6 +12,7 @@ if is_mri; then clone_repo "rspec-core" clone_repo "rspec-expectations" clone_repo "rspec-mocks" + clone_repo "rspec-rails" if rspec_support_compatible; then clone_repo "rspec-support" diff --git a/script/functions.sh b/script/functions.sh index fcc944c7f..ae0b74a80 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,11 +1,10 @@ -# This file was generated on 2014-03-14T11:33:06+13:00 from the rspec-dev repo. +# This file was generated on 2014-03-29T16:39:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` -BUNDLE_INSTALL_FLAGS=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` # Taken from: # https://github.com/travis-ci/travis-build/blob/e9314616e182a23e6a280199cd9070bfc7cae548/lib/travis/build/script/templates/header.sh#L34-L53 @@ -52,6 +51,12 @@ function is_mri_192 { fi } +function set_rails_version_for_rspec_rails { + if ruby -e "exit(RUBY_VERSION < '1.9.3')"; then + export RAILS_VERSION=3.2.17 + fi +} + function rspec_support_compatible { if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then return 0 @@ -123,7 +128,8 @@ function run_spec_suite_for { echo "Running specs for $1" echo unset BUNDLE_GEMFILE - travis_retry bundle install $BUNDLE_INSTALL_FLAGS + bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + travis_retry bundle install $bundle_install_flags run_specs_and_record_done popd fi; diff --git a/script/run_build b/script/run_build index 982d05faa..ca668d999 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-03-14T11:33:06+13:00 from the rspec-dev repo. +# This file was generated on 2014-03-29T16:39:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -18,6 +18,9 @@ if is_mri; then run_spec_suite_for "rspec-expectations" run_spec_suite_for "rspec-mocks" + set_rails_version_for_rspec_rails + run_spec_suite_for "rspec-rails" + if rspec_support_compatible; then run_spec_suite_for "rspec-support" fi From be01143249447a3ddc26e341bb40647acca2b30d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 30 Mar 2014 13:16:47 -0700 Subject: [PATCH 0584/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 8 +------- script/run_build | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8aa3f3291..0f7718476 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-03-29T16:39:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: "script/clone_all_rspec_repos" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 2d88979f8..9777ecfdd 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-03-29T16:39:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index ae0b74a80..759b543f9 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-03-29T16:39:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html @@ -51,12 +51,6 @@ function is_mri_192 { fi } -function set_rails_version_for_rspec_rails { - if ruby -e "exit(RUBY_VERSION < '1.9.3')"; then - export RAILS_VERSION=3.2.17 - fi -} - function rspec_support_compatible { if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then return 0 diff --git a/script/run_build b/script/run_build index ca668d999..de3644218 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-03-29T16:39:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -17,8 +17,6 @@ if is_mri; then run_spec_suite_for "rspec-core" run_spec_suite_for "rspec-expectations" run_spec_suite_for "rspec-mocks" - - set_rails_version_for_rspec_rails run_spec_suite_for "rspec-rails" if rspec_support_compatible; then From f0880f304e4bb9be3e91b846fcd0405bea5b1f1d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 31 Mar 2014 18:34:18 -0700 Subject: [PATCH 0585/1179] Get `and_call_original` to work properly on class hierarchies. For #613. --- lib/rspec/mocks/method_double.rb | 2 +- lib/rspec/mocks/proxy.rb | 73 +++++++++++++++++++- lib/rspec/mocks/space.rb | 6 ++ lib/rspec/mocks/verifying_proxy.rb | 5 ++ spec/rspec/mocks/stub_implementation_spec.rb | 46 +++++++++--- 5 files changed, 119 insertions(+), 13 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 28388eca5..45b7d47dd 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -25,7 +25,7 @@ def original_method # method handles for missing methods even if `respond_to?` is correct. @original_method ||= @method_stasher.original_method || - @proxy.method_handle_for(method_name) || + @proxy.original_method_handle_for(method_name) || Proc.new do |*args, &block| @object.__send__(:method_missing, @method_name, *args, &block) end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 2cb88a496..95bc470cd 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -37,7 +37,7 @@ def as_null_object end # @private - def method_handle_for(message) + def original_method_handle_for(message) nil end @@ -255,7 +255,7 @@ def reset # @private class PartialDoubleProxy < Proxy - def method_handle_for(message) + def original_method_handle_for(message) if any_instance_class_recorder_observing_method?(@object.class, message) message = ::RSpec::Mocks.space. any_instance_recorder_for(@object.class). @@ -301,6 +301,75 @@ def any_instance_class_recorder_observing_method?(klass, method_name) end end + # @private + # When we mock or stub a method on a class, we have to treat it a bit different, + # because normally singleton method definitions only affect the object on which + # they are defined, but on classes they affect subclasses, too. As a result, + # we need some special handling to get the original method. + module PartialClassDoubleProxyMethods + def initialize(source_space, *args) + @source_space = source_space + super(*args) + end + + # Consider this situation: + # + # class A; end + # class B < A; end + # + # allow(A).to receive(:new) + # expect(B).to receive(:new).and_call_original + # + # When getting the original definition for `B.new`, we cannot rely purely on + # using `B.method(:new)` before our redefinition is defined on `B`, because + # `B.method(:new)` will return a method that will execute the stubbed version + # of the method on `A` since singleton methods on classes are in the lookup + # hierarchy. + # + # To do it properly, we need to find the original definition of `new` from `A` + # from _before_ `A` was stubbed, and we need to rebind it to `B` so that it will + # run with the proper `self`. + # + # That's what this method (together with `original_unbound_method_handle_from_ancestor_for`) + # does. + def original_method_handle_for(message) + unbound_method = superclass_proxy && + superclass_proxy.original_unbound_method_handle_from_ancestor_for(message.to_sym) + + return super unless unbound_method + unbound_method.bind(object) + end + + protected + + def original_unbound_method_handle_from_ancestor_for(message) + method_double = @method_doubles.fetch(message) do + # The fact that there is no method double for this message indicates + # that it has not been redefined by rspec-mocks. We need to continue + # looking up the ancestor chain. + return superclass_proxy && + superclass_proxy.original_unbound_method_handle_from_ancestor_for(message) + end + + method_double.original_method.unbind + end + + def superclass_proxy + return @superclass_proxy if defined?(@superclass_proxy) + + if (superclass = object.superclass) + @superclass_proxy = @source_space.proxy_for(superclass) + else + @superclass_proxy = nil + end + end + end + + # @private + class PartialClassDoubleProxy < PartialDoubleProxy + include PartialClassDoubleProxyMethods + end + # @private class ProxyForNil < PartialDoubleProxy def initialize(order_group) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index f77baca8b..9aec66e9e 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -122,6 +122,12 @@ def proxy_not_found_for(id, object) proxies[id] = case object when NilClass then ProxyForNil.new(@expectation_ordering) when TestDouble then object.__build_mock_proxy(@expectation_ordering) + when Class + if RSpec::Mocks.configuration.verify_partial_doubles? + VerifyingPartialClassDoubleProxy.new(self, object, @expectation_ordering) + else + PartialClassDoubleProxy.new(self, object, @expectation_ordering) + end else if RSpec::Mocks.configuration.verify_partial_doubles? VerifyingPartialDoubleProxy.new(object, @expectation_ordering) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index b07cc8062..d9dce85d5 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -102,6 +102,11 @@ def method_reference end end + # @private + class VerifyingPartialClassDoubleProxy < VerifyingPartialDoubleProxy + include PartialClassDoubleProxyMethods + end + # @private class VerifyingMethodDouble < MethodDouble def initialize(object, method_name, proxy, method_reference) diff --git a/spec/rspec/mocks/stub_implementation_spec.rb b/spec/rspec/mocks/stub_implementation_spec.rb index 32e3b7484..0c94efeea 100644 --- a/spec/rspec/mocks/stub_implementation_spec.rb +++ b/spec/rspec/mocks/stub_implementation_spec.rb @@ -54,18 +54,44 @@ def obj.foo; :original; end expect(obj.foo(3)).to eq :three end - it "restores the correct implementations when stubbed and unstubbed on a parent and child class" do - parent = Class.new - child = Class.new(parent) + shared_examples_for "stubbing `new` on class objects" do + it "restores the correct implementations when stubbed and unstubbed on a parent and child class" do + parent = stub_const("Parent", Class.new) + child = stub_const("Child", Class.new(parent)) - allow(parent).to receive(:new) - allow(child).to receive(:new) - allow(parent).to receive(:new).and_call_original - allow(child).to receive(:new).and_call_original + allow(parent).to receive(:new) + allow(child).to receive(:new) + allow(parent).to receive(:new).and_call_original + allow(child).to receive(:new).and_call_original - expect(parent.new).to be_an_instance_of parent - pending "not working for `and_call_original` yet, but works with `unstub`" - expect(child.new).to be_an_instance_of child + expect(parent.new).to be_an_instance_of parent + expect(child.new).to be_an_instance_of child + end + + it "restores the correct implementations when stubbed and unstubbed on a grandparent and grandchild class" do + grandparent = stub_const("GrandParent", Class.new) + parent = stub_const("Parent", Class.new(grandparent)) + child = stub_const("Child", Class.new(parent)) + + allow(grandparent).to receive(:new) + allow(child).to receive(:new) + allow(grandparent).to receive(:new).and_call_original + allow(child).to receive(:new).and_call_original + + expect(grandparent.new).to be_an_instance_of grandparent + expect(child.new).to be_an_instance_of child + end + end + + context "when partial doubles are not verified" do + before { expect(RSpec::Mocks.configuration.verify_partial_doubles?).to be false } + include_examples "stubbing `new` on class objects" + end + + context "when partial doubles are verified" do + include_context "with isolated configuration" + before { RSpec::Mocks.configuration.verify_partial_doubles = true } + include_examples "stubbing `new` on class objects" end end end From 9ec11323ab90704d69faf7cfc7a7d30724f038f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 1 Apr 2014 08:43:28 -0700 Subject: [PATCH 0586/1179] Changelog for #649. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 6a74198ef..4f0127ae3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -35,6 +35,8 @@ Bug Fixes: private methods if `verify_partial_doubles` was configured. (Oren Dobzinski) * Include useful error message when trying to observe an unimplemented method on an any instance. (Xavier Shay) +* Fix `and_call_original` to work properly when multiple classes in an + inheritance hierarchy have been stubbed with the same method. (Myron Marston) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) From 66055a6afd0cf09e9d57c1c9971348860cd0d685 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 2 Apr 2014 12:10:11 -0700 Subject: [PATCH 0587/1179] Refactor: centralize location/expected_from. --- lib/rspec/mocks.rb | 10 ++-------- lib/rspec/mocks/any_instance/expectation_chain.rb | 6 ++++-- lib/rspec/mocks/any_instance/stub_chain.rb | 6 ++++-- lib/rspec/mocks/matchers/receive.rb | 3 +-- lib/rspec/mocks/proxy.rb | 10 ++++++---- lib/rspec/mocks/syntax.rb | 5 +---- lib/rspec/mocks/verifying_proxy.rb | 4 ++-- 7 files changed, 20 insertions(+), 24 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index ca518a0af..17b2831bc 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -67,10 +67,7 @@ def self.teardown # x = 0 # RSpec::Mocks.allow_message(bar, :foo) { x += 1 } def self.allow_message(subject, message, opts={}, &block) - orig_caller = opts.fetch(:expected_from) { - CallerFilter.first_non_rspec_line - } - space.proxy_for(subject).add_stub(orig_caller, message, opts, &block) + space.proxy_for(subject).add_stub(message, opts, &block) end # Sets a message expectation on `subject`. @@ -85,10 +82,7 @@ def self.allow_message(subject, message, opts={}, &block) # RSpec::Mocks.expect_message(bar, :foo) # bar.foo def self.expect_message(subject, message, opts={}, &block) - orig_caller = opts.fetch(:expected_from) { - CallerFilter.first_non_rspec_line - } - space.proxy_for(subject).add_message_expectation(orig_caller, message, opts, &block) + space.proxy_for(subject).add_message_expectation(message, opts, &block) end # Call the passed block and verify mocks after it has executed. This allows diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index ff85dfc98..153f37be1 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -25,8 +25,10 @@ class PositiveExpectationChain < ExpectationChain def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.space.proxy_for(instance) - expected_from = IGNORED_BACKTRACE_LINE - me = proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block) + method_name, opts = @expectation_args + opts = (opts || {}).merge(:expected_form => IGNORED_BACKTRACE_LINE) + + me = proxy.add_message_expectation(method_name, opts, &@expectation_block) if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? me.and_yield_receiver_to_implementation end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index e7459c4a1..16071eca9 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -13,8 +13,10 @@ def expectation_fulfilled? def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.space.proxy_for(instance) - expected_from = IGNORED_BACKTRACE_LINE - stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block) + method_name, opts = @expectation_args + opts = (opts || {}).merge(:expected_form => IGNORED_BACKTRACE_LINE) + + stub = proxy.add_stub(method_name, opts, &@expectation_block) @recorder.stubs[stub.message] << stub if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 9a12fab16..be41e1996 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -9,7 +9,6 @@ def initialize(message, block) @message = message @block = block @recorded_customizations = [] - @backtrace_line = CallerFilter.first_non_rspec_line end def name @@ -74,7 +73,7 @@ def warn_if_any_instance(expression, subject) def setup_mock_proxy_method_substitute(subject, method, block) proxy = ::RSpec::Mocks.space.proxy_for(subject) - setup_method_substitute(proxy, method, block, @backtrace_line) + setup_method_substitute(proxy, method, block) end def setup_any_instance_method_substitute(subject, method, block) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 95bc470cd..c1719b8bc 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -42,7 +42,8 @@ def original_method_handle_for(message) end # @private - def add_message_expectation(location, method_name, opts={}, &block) + def add_message_expectation(method_name, opts={}, &block) + location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } meth_double = method_double_for(method_name) if null_object? && !block @@ -101,7 +102,8 @@ def check_for_unexpected_arguments(expectation) end # @private - def add_stub(location, method_name, opts={}, &implementation) + def add_stub(method_name, opts={}, &implementation) + location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation end @@ -380,7 +382,7 @@ def initialize(order_group) attr_accessor :warn_about_expectations alias warn_about_expectations? warn_about_expectations - def add_message_expectation(location, method_name, opts={}, &block) + def add_message_expectation(method_name, opts={}, &block) warn(method_name) if warn_about_expectations? super end @@ -390,7 +392,7 @@ def add_negative_message_expectation(location, method_name, &implementation) super end - def add_stub(location, method_name, opts={}, &implementation) + def add_stub(method_name, opts={}, &implementation) warn(method_name) if warn_about_expectations? super end diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 0f32d680f..8d780c5eb 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -30,14 +30,12 @@ def self.enable_should(syntax_host = default_should_syntax_host) syntax_host.class_exec do def should_receive(message, opts={}, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) - opts[:expected_from] ||= CallerFilter.first_non_rspec_line ::RSpec::Mocks.expect_message(self, message, opts, &block) end def should_not_receive(message, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) - opts = {:expected_from => CallerFilter.first_non_rspec_line} - ::RSpec::Mocks.expect_message(self, message, opts, &block).never + ::RSpec::Mocks.expect_message(self, message, {}, &block).never end def stub(message_or_hash, opts={}, &block) @@ -45,7 +43,6 @@ def stub(message_or_hash, opts={}, &block) if ::Hash === message_or_hash message_or_hash.each {|message, value| stub(message).and_return value } else - opts[:expected_from] = CallerFilter.first_non_rspec_line ::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block) end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index d9dce85d5..53e86c0ff 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -6,7 +6,7 @@ module Mocks # @private module VerifyingProxyMethods - def add_stub(location, method_name, opts={}, &implementation) + def add_stub(method_name, opts={}, &implementation) ensure_implemented(method_name) super end @@ -16,7 +16,7 @@ def add_simple_stub(method_name, *args) super end - def add_message_expectation(location, method_name, opts={}, &block) + def add_message_expectation(method_name, opts={}, &block) ensure_implemented(method_name) super end From 37447c351a614e102b9bb78cc81b6a44e367a81f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 2 Apr 2014 16:19:03 -0700 Subject: [PATCH 0588/1179] Add failing spec. --- spec/rspec/mocks/any_instance_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 27f0d31e0..05506bbb8 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -167,6 +167,15 @@ def private_method; :private_method_return_value; end expect(klass.new.foo).to be(return_value) expect(klass.new.foo).to be(return_value) end + + it "can change how instances responds in the middle of an example" do + instance = klass.new + + allow_any_instance_of(klass).to receive(:foo).and_return(1) + expect(instance.foo).to eq(1) + allow_any_instance_of(klass).to receive(:foo).and_return(2) + expect(instance.foo).to eq(2) + end end context "with #and_yield" do From e00d6d7cf0f044bc9bd55d1defbbb433a88d67a4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 2 Apr 2014 16:26:33 -0700 Subject: [PATCH 0589/1179] Make `any_instance` update existing instance stubs. Fixes #613. --- Changelog.md | 2 + lib/rspec/mocks/any_instance.rb | 1 + lib/rspec/mocks/any_instance/proxy.rb | 102 ++++++++++++++++++ lib/rspec/mocks/any_instance/recorder.rb | 15 +-- lib/rspec/mocks/matchers/receive.rb | 6 +- .../mocks/matchers/receive_message_chain.rb | 8 +- lib/rspec/mocks/matchers/receive_messages.rb | 2 +- lib/rspec/mocks/method_double.rb | 7 +- lib/rspec/mocks/proxy.rb | 4 +- lib/rspec/mocks/space.rb | 8 ++ lib/rspec/mocks/syntax.rb | 2 +- spec/rspec/mocks/any_instance_spec.rb | 29 ++++- .../matchers/receive_message_chain_spec.rb | 20 ++++ .../mocks/matchers/receive_messages_spec.rb | 9 ++ spec/rspec/mocks/matchers/receive_spec.rb | 10 +- spec/rspec/mocks/should_syntax_spec.rb | 38 ++++++- 16 files changed, 227 insertions(+), 36 deletions(-) create mode 100644 lib/rspec/mocks/any_instance/proxy.rb diff --git a/Changelog.md b/Changelog.md index 4f0127ae3..e3e845e31 100644 --- a/Changelog.md +++ b/Changelog.md @@ -37,6 +37,8 @@ Bug Fixes: on an any instance. (Xavier Shay) * Fix `and_call_original` to work properly when multiple classes in an inheritance hierarchy have been stubbed with the same method. (Myron Marston) +* Fix `any_instance` so that it updates existing instances that have + already been stubbed. (Myron Marston) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) diff --git a/lib/rspec/mocks/any_instance.rb b/lib/rspec/mocks/any_instance.rb index 1cae54ae6..bc912d84b 100644 --- a/lib/rspec/mocks/any_instance.rb +++ b/lib/rspec/mocks/any_instance.rb @@ -6,4 +6,5 @@ any_instance/expectation_chain any_instance/message_chains any_instance/recorder + any_instance/proxy ].each { |f| RSpec::Support.require_rspec_mocks(f) } diff --git a/lib/rspec/mocks/any_instance/proxy.rb b/lib/rspec/mocks/any_instance/proxy.rb new file mode 100644 index 000000000..c6d4eabe5 --- /dev/null +++ b/lib/rspec/mocks/any_instance/proxy.rb @@ -0,0 +1,102 @@ +module RSpec + module Mocks + module AnyInstance + # @private + # The `AnyInstance::Recorder` is responsible for redefining the klass's + # instance method in order to add any stubs/expectations the first time + # the method is called. It's not capable of updating a stub on an instance + # that's already been previously stubbed (either directly, or via + # `any_instance`). + # + # This proxy sits in front of the recorder and delegates both to it + # and to the `RSpec::Mocks::Proxy` for each already mocked or stubbed + # instance of the class, in order to propogates changes to the instances. + # + # Note that unlike `RSpec::Mocks::Proxy`, this proxy class is stateless + # and is not persisted in `RSpec::Mocks.space`. + # + # Proxying for the message expectation fluent interface (typically chained + # off of the return value of one of these methods) is provided by the + # `FluentInterfaceProxy` class below. + class Proxy + def initialize(recorder, target_proxies) + @recorder = recorder + @target_proxies = target_proxies + end + + def klass + @recorder.klass + end + + def stub(method_name_or_method_map, &block) + if Hash === method_name_or_method_map + method_name_or_method_map.each do |method_name, return_value| + stub(method_name).and_return(return_value) + end + else + perform_proxying(__method__, [method_name_or_method_map], block) do |proxy| + proxy.add_stub(method_name_or_method_map, &block) + end + end + end + + def unstub(method_name) + perform_proxying(__method__, [method_name], nil) do |proxy| + proxy.remove_stub_if_present(method_name) + end + end + + def stub_chain(*chain, &block) + perform_proxying(__method__, chain, block) do |proxy| + Mocks::StubChain.stub_chain_on(proxy.object, *chain, &block) + end + end + + def expect_chain(*chain, &block) + perform_proxying(__method__, chain, block) do |proxy| + Mocks::ExpectChain.expect_chain_on(proxy.object, *chain, &block) + end + end + + def should_receive(method_name, &block) + perform_proxying(__method__, [method_name], block) do |proxy| + proxy.add_message_expectation(method_name, &block) + end + end + + def should_not_receive(method_name, &block) + perform_proxying(__method__, [method_name], block) do |proxy| + proxy.add_message_expectation(method_name, &block).never + end + end + + private + + def perform_proxying(method_name, args, block, &target_proxy_block) + recorder_value = @recorder.__send__(method_name, *args, &block) + proxy_values = @target_proxies.map(&target_proxy_block) + FluentInterfaceProxy.new([recorder_value] + proxy_values) + end + end + + # @private + # Delegates messages to each of the given targets in order to + # provide the fluent interface that is available off of message + # expectations when dealing with `any_instance`. + # + # `targets` will typically contain 1 of the `AnyInstance::Recorder` + # return values and N `MessageExpectation` instances (one per instance + # of the `any_instance` klass). + class FluentInterfaceProxy + def initialize(targets) + @targets = targets + end + + def method_missing(*args, &block) + return_values = @targets.map { |t| t.__send__(*args, &block) } + FluentInterfaceProxy.new(return_values) + end + end + end + end +end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index b43ea2242..69f773280 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -26,15 +26,9 @@ def initialize(klass) # instance of this object that invokes the submitted method. # # @see Methods#stub - def stub(method_name_or_method_map, &block) - if Hash === method_name_or_method_map - method_name_or_method_map.each do |method_name, return_value| - stub(method_name).and_return(return_value) - end - else - observe!(method_name_or_method_map) - message_chains.add(method_name_or_method_map, StubChain.new(self, method_name_or_method_map, &block)) - end + def stub(method_name, &block) + observe!(method_name) + message_chains.add(method_name, StubChain.new(self, method_name, &block)) end # Initializes the recording a stub chain to be played back against any @@ -85,9 +79,6 @@ def unstub(method_name) raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" end message_chains.remove_stub_chains_for!(method_name) - ::RSpec::Mocks.space.proxies_of(@klass).each do |proxy| - stubs[method_name].each { |stub| proxy.remove_single_stub(method_name, stub) } - end stubs[method_name].clear stop_observing!(method_name) unless message_chains.has_expectation?(method_name) end diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index be41e1996..e5ff3941f 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -61,7 +61,7 @@ def setup_any_instance_allowance(subject, &block) private def warn_if_any_instance(expression, subject) - if AnyInstance::Recorder === subject + if AnyInstance::Proxy === subject RSpec.warning( "`#{expression}(#{subject.klass}.any_instance).to` " << "is probably not what you meant, it does not operate on " << @@ -77,8 +77,8 @@ def setup_mock_proxy_method_substitute(subject, method, block) end def setup_any_instance_method_substitute(subject, method, block) - any_instance_recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject) - setup_method_substitute(any_instance_recorder, method, block) + proxy = ::RSpec::Mocks.space.any_instance_proxy_for(subject) + setup_method_substitute(proxy, method, block) end def setup_method_substitute(host, method, block, *args) diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index 51930371f..39365ee4c 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -28,14 +28,14 @@ def setup_allowance(subject, &block) end def setup_any_instance_allowance(subject, &block) - recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject) - chain = recorder.stub_chain(*@chain, &(@block || block)) + proxy = ::RSpec::Mocks.space.any_instance_proxy_for(subject) + chain = proxy.stub_chain(*@chain, &(@block || block)) replay_customizations(chain) end def setup_any_instance_expectation(subject, &block) - recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject) - chain = recorder.expect_chain(*@chain, &(@block || block)) + proxy = ::RSpec::Mocks.space.any_instance_proxy_for(subject) + chain = proxy.expect_chain(*@chain, &(@block || block)) replay_customizations(chain) end diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index 52c7f9bc6..1fcb03d04 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -58,7 +58,7 @@ def proxy_on(subject) end def any_instance_of(subject) - ::RSpec::Mocks.space.any_instance_recorder_for(subject) + ::RSpec::Mocks.space.any_instance_proxy_for(subject) end def each_message_on(host) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 45b7d47dd..785423c4b 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -189,13 +189,12 @@ def add_default_stub(*args, &implementation) # @private def remove_stub raise_method_not_stubbed_error if stubs.empty? - expectations.empty? ? reset : stubs.clear + remove_stub_if_present end # @private - def remove_single_stub(stub) - stubs.delete(stub) - restore_original_method if stubs.empty? && expectations.empty? + def remove_stub_if_present + expectations.empty? ? reset : stubs.clear end # @private diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index c1719b8bc..8c2a0abfd 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -118,8 +118,8 @@ def remove_stub(method_name) end # @private - def remove_single_stub(method_name, stub) - method_double_for(method_name).remove_single_stub(stub) + def remove_stub_if_present(method_name) + method_double_for(method_name).remove_stub_if_present end # @private diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 9aec66e9e..d8a1bbc49 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -13,6 +13,10 @@ def any_instance_recorder_for(*args) raise_lifecycle_message end + def any_instance_proxy_for(*args) + raise_lifecycle_message + end + def register_constant_mutator(mutator) raise_lifecycle_message end @@ -85,6 +89,10 @@ def any_instance_recorder_for(klass) end end + def any_instance_proxy_for(klass) + AnyInstance::Proxy.new(any_instance_recorder_for(klass), proxies_of(klass)) + end + def proxies_of(klass) proxies.values.select { |proxy| klass === proxy.object } end diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 8d780c5eb..ebd20994e 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -77,7 +77,7 @@ def received_message?(message, *args, &block) Class.class_exec do def any_instance ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) - ::RSpec::Mocks.space.any_instance_recorder_for(self) + ::RSpec::Mocks.space.any_instance_proxy_for(self) end end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 05506bbb8..8b691ec6b 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -175,6 +175,8 @@ def private_method; :private_method_return_value; end expect(instance.foo).to eq(1) allow_any_instance_of(klass).to receive(:foo).and_return(2) expect(instance.foo).to eq(2) + allow_any_instance_of(klass).to receive(:foo).and_raise("boom") + expect { instance.foo }.to raise_error("boom") end end @@ -308,7 +310,6 @@ class RSpec::SampleRspecTestClass;end obj.existing_method allow_any_instance_of(klass).to receive(:existing_method).and_call_original - pending "not working for `and_call_original` yet, but works with `unstub`" expect(obj.existing_method).to eq(:existing_method_return_value) end @@ -318,16 +319,15 @@ class RSpec::SampleRspecTestClass;end expect(obj.existing_method).to eq(:any_instance_value) allow_any_instance_of(klass).to receive(:existing_method).and_call_original - pending "not working for `and_call_original` yet, but works with `unstub`" expect(obj.existing_method).to eq(:existing_method_return_value) end - it "does not remove any stubs set directly on an instance" do + it "removes any stubs set directly on an instance" do allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = klass.new allow(obj).to receive(:existing_method).and_return(:local_method) allow_any_instance_of(klass).to receive(:existing_method).and_call_original - expect(obj.existing_method).to eq(:local_method) + expect(obj.existing_method).to eq(:existing_method_return_value) end it "does not remove any expectations with the same method name" do @@ -360,6 +360,15 @@ class RSpec::SampleRspecTestClass;end expect { klass.new.another_existing_method }.to_not raise_error end + it "affects previously stubbed instances" do + instance = klass.new + + allow_any_instance_of(klass).to receive(:foo).and_return(1) + expect(instance.foo).to eq(1) + expect_any_instance_of(klass).not_to receive(:foo) + expect { instance.foo }.to fail + end + context "with constraints" do it "fails if the method is called with the specified parameters" do expect_any_instance_of(klass).not_to receive(:existing_method_with_arguments).with(:argument_one, :argument_two) @@ -409,6 +418,18 @@ def inspect reset_all end + it "affects previously stubbed instances" do + instance = klass.new + + allow_any_instance_of(klass).to receive(:foo).and_return(1) + expect(instance.foo).to eq(1) + expect_any_instance_of(klass).to receive(:foo).with(2).and_return(2) + expect(instance.foo(2)).to eq(2) + + # TODO: this shouldn't be necessary to satisfy the expectation, but is. + klass.new.foo(2) + end + context "with an expectation is set on a method which does not exist" do it "returns the expected value" do expect_any_instance_of(klass).to receive(:foo).and_return(1) diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index bac1ef88a..3931ac62d 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -143,6 +143,15 @@ module RSpec::Mocks::Matchers expect(o.to_a.length).to eq(3) end + it "stubs already stubbed instances when using `allow_any_instance_of`" do + o = Object.new + allow(o).to receive(:foo).and_return(dbl = double) + expect(o.foo).to be(dbl) + + allow_any_instance_of(Object).to receive_message_chain(:foo, :bar).and_return("bazz") + expect(o.foo.bar).to eq("bazz") + end + it "fails when with expect_any_instance_of is used and the entire chain is not called" do expect { expect_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) @@ -150,6 +159,17 @@ module RSpec::Mocks::Matchers }.to raise_error(RSpec::Mocks::MockExpectationError) end + it "affects previously stubbed instances when `expect_any_instance_of` is called" do + o = Object.new + allow(o).to receive(:foo).and_return(double) + + expect_any_instance_of(Object).to receive_message_chain(:foo, :bar => 3) + expect(o.foo.bar).to eq(3) + + # TODO: this shouldn't be necessary to satisfy the expectation, but is. + Object.new.foo.bar + end + it "passes when with expect_any_instance_of is used and the entire chain is called" do o = Object.new diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 28484cc52..4155aab89 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -52,6 +52,15 @@ module Mocks expect(obj.b).to eq 2 end + it "updates stubs on instances with existing stubs" do + allow(obj).to receive(:a).and_return(3) + expect(obj.a).to eq(3) + + allow_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) + expect(obj.a).to eq 1 + expect(obj.b).to eq 2 + end + it_behaves_like "complains when given blocks" end diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 6cc552d10..c36e120f5 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -18,14 +18,16 @@ module Mocks it "warns about expect(Klass.any_instance).to receive..." do expect(RSpec).to receive(:warning).with(/expect.*any_instance.*is probably not what you meant.*expect_any_instance_of.*instead/) - expect(Object.any_instance).to receive(:foo) - Object.any_instance.foo + any_instance_proxy = Object.any_instance + expect(any_instance_proxy).to receive(:foo) + any_instance_proxy.foo end it "includes the correct call site in the expect warning" do + any_instance_proxy = Object.any_instance expect_warning_with_call_site(__FILE__, __LINE__ + 1) - expect(Object.any_instance).to receive(:foo) - Object.any_instance.foo + expect(any_instance_proxy).to receive(:foo) + any_instance_proxy.foo end end diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index ed9f6e165..59738a962 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -244,6 +244,25 @@ def use_rspec_mocks expect { verify_all }.to raise_error(RSpec::Mocks::MockExpectationError) end + it 'affects previously stubbed instances when stubbing a method' do + instance = klass.new + klass.any_instance.stub(:foo).and_return(2) + expect(instance.foo).to eq(2) + klass.any_instance.stub(:foo).and_return(1) + expect(instance.foo).to eq(1) + end + + it 'affects previously stubbed instances when mocking a method' do + instance = klass.new + klass.any_instance.stub(:foo).and_return(2) + expect(instance.foo).to eq(2) + klass.any_instance.should_receive(:foo).and_return(1) + expect(instance.foo).to eq(1) + + # TODO: this shouldn't be necessary to satisfy the expectation, but is. + klass.new.foo(2) + end + context "invocation order" do describe "#stub" do it "raises an error if 'stub' follows 'with'" do @@ -296,6 +315,15 @@ def use_rspec_mocks expect(klass.new.one.two.three).to eq(:four) end end + + it 'affects previously stubbed instances' do + instance = klass.new + dbl = double + klass.any_instance.stub(:foo).and_return(dbl) + expect(instance.foo).to eq(dbl) + klass.any_instance.stub_chain(:foo, :bar => 3) + expect(instance.foo.bar).to eq(3) + end end describe "#should_receive" do @@ -379,11 +407,19 @@ def use_rspec_mocks expect(obj.existing_method).to eq(:existing_method_return_value) end - it "does not remove any stubs set directly on an instance" do + it "removes stubs set directly on an instance" do klass.any_instance.stub(:existing_method).and_return(:any_instance_value) obj = klass.new obj.stub(:existing_method).and_return(:local_method) klass.any_instance.unstub(:existing_method) + expect(obj.existing_method).to eq(:existing_method_return_value) + end + + it "does not remove message expectations set directly on an instance" do + klass.any_instance.stub(:existing_method).and_return(:any_instance_value) + obj = klass.new + obj.should_receive(:existing_method).and_return(:local_method) + klass.any_instance.unstub(:existing_method) expect(obj.existing_method).to eq(:local_method) end From 62ad32162af34f096b679f6b53c2ab9001e1ce8c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 2 Apr 2014 23:46:31 -0700 Subject: [PATCH 0590/1179] Implement `respond_to?` properly. --- lib/rspec/mocks/any_instance/proxy.rb | 10 ++++++++++ spec/rspec/mocks/should_syntax_spec.rb | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/lib/rspec/mocks/any_instance/proxy.rb b/lib/rspec/mocks/any_instance/proxy.rb index c6d4eabe5..20c4519b7 100644 --- a/lib/rspec/mocks/any_instance/proxy.rb +++ b/lib/rspec/mocks/any_instance/proxy.rb @@ -92,6 +92,16 @@ def initialize(targets) @targets = targets end + if RUBY_VERSION.to_f > 1.8 + def respond_to_missing?(method_name, include_private = false) + super || @targets.first.respond_to?(method_name, include_private) + end + else + def respond_to?(method_name, include_private = false) + super || @targets.first.respond_to?(method_name, include_private) + end + end + def method_missing(*args, &block) return_values = @targets.map { |t| t.__send__(*args, &block) } FluentInterfaceProxy.new(return_values) diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index 59738a962..d9aa882c1 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -244,6 +244,13 @@ def use_rspec_mocks expect { verify_all }.to raise_error(RSpec::Mocks::MockExpectationError) end + it 'can get method objects for the fluent interface', :if => RUBY_VERSION.to_f > 1.8 do + and_return = klass.any_instance.stub(:foo).method(:and_return) + and_return.call(23) + + expect(klass.new.foo).to eq(23) + end + it 'affects previously stubbed instances when stubbing a method' do instance = klass.new klass.any_instance.stub(:foo).and_return(2) From de63b7d982cb260c25b0f01d1956682cfe23e71a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 3 Apr 2014 18:02:53 -0700 Subject: [PATCH 0591/1179] Implement `respond_to?` on the example proxy. This makes the object a better behaved object that accurately reports what it responds to. It's also necessary for https://github.com/rspec/rspec-expectations/pull/513 --- spec/rspec/mocks/partial_double_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 4222ccd6d..8622a82ae 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -128,6 +128,10 @@ def proxied? true end + def respond_to?(name, include_all=false) + super || name == :proxied? || @target.respond_to?(name, include_all) + end + def method_missing(*a) @target.send(*a) end From fa37fbf5c1a454c0fc22dee458aa625a3dd35bdb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 26 Apr 2014 23:32:44 -0700 Subject: [PATCH 0592/1179] Pass the double name through to the proxy for verified doubles. This ensures that failure messages mention name the specific double. --- lib/rspec/mocks/verifying_double.rb | 4 +-- lib/rspec/mocks/verifying_proxy.rb | 6 ++-- spec/rspec/mocks/verifying_double_spec.rb | 34 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index ee35a24a8..3a3d288d7 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -73,7 +73,7 @@ def initialize(doubled_module, *args) end def __build_mock_proxy(order_group) - VerifyingProxy.new(self, order_group, + VerifyingProxy.new(self, order_group, @name, @doubled_module, InstanceMethodReference ) @@ -94,7 +94,7 @@ def initialize(doubled_module, *args) end def __build_mock_proxy(order_group) - VerifyingProxy.new(self, order_group, + VerifyingProxy.new(self, order_group, @name, @doubled_module, ObjectMethodReference ) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 53e86c0ff..99b18a8ec 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -57,10 +57,10 @@ def ensure_publicly_implemented(method_name, object) class VerifyingProxy < Proxy include VerifyingProxyMethods - def initialize(object, order_group, name, method_reference_class) - super(object, order_group) + def initialize(object, order_group, name, doubled_module, method_reference_class) + super(object, order_group, name) @object = object - @doubled_module = name + @doubled_module = doubled_module @method_reference_class = method_reference_class # A custom method double is required to pass through a way to lookup diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index ac538b2d6..a91863d35 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -76,6 +76,13 @@ module Mocks RSpec::Mocks.configuration.verify_doubled_constant_names = false end + it 'includes the double name in errors for unexpected messages' do + o = instance_double("NonLoadedClass") + expect { + o.foo + }.to fail_matching('Double "NonLoadedClass (instance)"') + end + it 'allows any instance method to be stubbed' do o = instance_double('NonloadedClass') allow(o).to receive(:undefined_instance_method).with(:arg).and_return(true) @@ -121,6 +128,13 @@ module Mocks RSpec::Mocks.configuration.verify_doubled_constant_names = true end + it 'includes the double name in errors for unexpected messages' do + o = instance_double("LoadedClass") + expect { + o.defined_instance_method + }.to fail_matching('Double "LoadedClass (instance)"') + end + it 'only allows instance methods that exist to be stubbed' do o = instance_double('LoadedClass', :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) @@ -256,6 +270,12 @@ module Mocks prevents { o.__send__(:undefined_method) } end + it "includes the double's name in a private method error" do + expect { + o.rand + }.to raise_error(NoMethodError, %r{private.*Double "LoadedClass \(instance\)"}) + end + it 'reports what public messages it responds to accurately' do expect(o.respond_to?(:defined_instance_method)).to be true expect(o.respond_to?(:defined_instance_method, true)).to be true @@ -309,6 +329,13 @@ module Mocks RSpec::Mocks.configuration.verify_doubled_constant_names = false end + it 'includes the double name in errors for unexpected messages' do + o = class_double("NonLoadedClass") + expect { + o.foo + }.to fail_matching('Double "NonLoadedClass"') + end + it 'allows any method to be stubbed' do o = class_double('NonloadedClass') allow(o).to receive(:undefined_instance_method).with(:arg).and_return(1) @@ -348,6 +375,13 @@ def use; end RSpec::Mocks.configuration.verify_doubled_constant_names = true end + it 'includes the double name in errors for unexpected messages' do + o = class_double("LoadedClass") + expect { + o.defined_class_method + }.to fail_matching('Double "LoadedClass"') + end + it 'only allows class methods that exist to be stubbed' do o = class_double('LoadedClass', :defined_class_method => 1) expect(o.defined_class_method).to eq(1) From b5106c90de85b4f00efd604fc614d2efcaec07b8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 27 Apr 2014 07:45:09 -0700 Subject: [PATCH 0593/1179] Changelog for #659. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index e3e845e31..c156d544a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -39,6 +39,8 @@ Bug Fixes: inheritance hierarchy have been stubbed with the same method. (Myron Marston) * Fix `any_instance` so that it updates existing instances that have already been stubbed. (Myron Marston) +* Fix verified doubles so that their class name is included in failure + messages. (Myron Marston) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) From 7acbc99f7429f049a2b54743b7de342dc053547d Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 6 Apr 2014 12:54:21 +0200 Subject: [PATCH 0594/1179] Make proxies notify any instance recorders when they receive messages. --- lib/rspec/mocks/any_instance/recorder.rb | 5 +++++ lib/rspec/mocks/proxy.rb | 6 ++++++ lib/rspec/mocks/space.rb | 17 +++++++++++++++-- lib/rspec/mocks/verifying_proxy.rb | 4 ++-- spec/rspec/mocks/any_instance_spec.rb | 9 +++++++-- spec/rspec/mocks/should_syntax_spec.rb | 3 --- 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 69f773280..1d49d3bb2 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -121,6 +121,11 @@ def already_observing?(method_name) @observed_methods.include?(method_name) || super_class_observing?(method_name) end + # @private + def notify_received_message(object, message, args, blk) + received_expected_message!(message) if message_chains.has_expectation?(message) + end + protected def stop_observing!(method_name) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 8c2a0abfd..0e279a064 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -18,6 +18,7 @@ def initialize(object, order_group, name=nil, options={}) @options = options @null_object = false @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) } + @subscribers = options.fetch(:subscribers, []) end # @private @@ -151,6 +152,11 @@ def record_message_received(message, *args, &block) # @private def message_received(message, *args, &block) record_message_received message, *args, &block + + @subscribers.each do |subscriber| + subscriber.notify_received_message(object, message, args, block) + end + expectation = find_matching_expectation(message, *args) stub = find_matching_method_stub(message, *args) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index d8a1bbc49..76d22a495 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -112,6 +112,10 @@ def registered?(object) private + def any_instance_recorders_from_ancestry_of(object) + [any_instance_recorders[object.class.__id__]].compact + end + # We don't want to depend on the stdlib ourselves, but if the user is # using threads then a Mutex will be available to us. If not, we don't # need to synchronize anyway. @@ -138,9 +142,18 @@ def proxy_not_found_for(id, object) end else if RSpec::Mocks.configuration.verify_partial_doubles? - VerifyingPartialDoubleProxy.new(object, @expectation_ordering) + VerifyingPartialDoubleProxy.new( + object, + @expectation_ordering, + :subscribers => any_instance_recorders_from_ancestry_of(object) + ) else - PartialDoubleProxy.new(object, @expectation_ordering) + PartialDoubleProxy.new( + object, + @expectation_ordering, + nil, + :subscribers => any_instance_recorders_from_ancestry_of(object) + ) end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 99b18a8ec..b3a4a4923 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -86,8 +86,8 @@ def visibility_for(method_name) class VerifyingPartialDoubleProxy < PartialDoubleProxy include VerifyingProxyMethods - def initialize(object, expectation_ordering) - super(object, expectation_ordering) + def initialize(object, expectation_ordering, options={}) + super(object, expectation_ordering, nil, options) @doubled_module = DirectObjectReference.new(object) # A custom method double is required to pass through a way to lookup diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 8b691ec6b..810848beb 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -425,9 +425,14 @@ def inspect expect(instance.foo).to eq(1) expect_any_instance_of(klass).to receive(:foo).with(2).and_return(2) expect(instance.foo(2)).to eq(2) + end - # TODO: this shouldn't be necessary to satisfy the expectation, but is. - klass.new.foo(2) + it "does not modify the return value of stubs set on an instance" do + expect_any_instance_of(Object).to receive(:foo).twice + object = Object.new + allow(object).to receive(:foo).and_return(3) + expect(object.foo).to eq(3) + expect(object.foo).to eq(3) end context "with an expectation is set on a method which does not exist" do diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index d9aa882c1..dfe7aa707 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -265,9 +265,6 @@ def use_rspec_mocks expect(instance.foo).to eq(2) klass.any_instance.should_receive(:foo).and_return(1) expect(instance.foo).to eq(1) - - # TODO: this shouldn't be necessary to satisfy the expectation, but is. - klass.new.foo(2) end context "invocation order" do From a8e1e657361330be36340c42bcb451fc9edd4861 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 30 Apr 2014 00:32:46 -0700 Subject: [PATCH 0595/1179] Ensure we take the class hierarchy into account when finding any_instance recorders. --- lib/rspec/mocks/space.rb | 4 +++- spec/rspec/mocks/any_instance_spec.rb | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 76d22a495..f6c6b08d4 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -113,7 +113,9 @@ def registered?(object) private def any_instance_recorders_from_ancestry_of(object) - [any_instance_recorders[object.class.__id__]].compact + object.class.ancestors.map do |klass| + any_instance_recorders[klass.__id__] + end.compact end # We don't want to depend on the stdlib ourselves, but if the user is diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 810848beb..e12aa2a1c 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -435,6 +435,28 @@ def inspect expect(object.foo).to eq(3) end + it "does not modify the return value of stubs set on an instance of a subclass" do + subklass = Class.new(klass) + subinstance = subklass.new + allow_any_instance_of(klass).to receive(:foo).and_return(1) + expect(subinstance.foo).to eq(1) + expect_any_instance_of(klass).to receive(:foo).with(2).and_return(2) + expect(subinstance.foo(2)).to eq(2) + end + + it "properly notifies any instance recorders at multiple levels of hierarchy" do + subclass = Class.new(klass) + instance = subclass.new + + expect_any_instance_of(klass).to receive(:msg_1) + expect_any_instance_of(subclass).to receive(:msg_2) + + allow(instance).to receive_messages(:msg_1 => "a", :msg_2 => "b") + + expect(instance.msg_1).to eq("a") + expect(instance.msg_2).to eq("b") + end + context "with an expectation is set on a method which does not exist" do it "returns the expected value" do expect_any_instance_of(klass).to receive(:foo).and_return(1) From 9cafc856af3fe4c62dd58dfe8931c04400d22ecd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 30 Apr 2014 00:41:44 -0700 Subject: [PATCH 0596/1179] Ensure our any_instance notification works when the mock proxy was created previously. --- lib/rspec/mocks/proxy.rb | 3 +-- lib/rspec/mocks/space.rb | 27 ++++++++++++++------------- lib/rspec/mocks/verifying_proxy.rb | 4 ++-- spec/rspec/mocks/any_instance_spec.rb | 9 +++++++++ 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 0e279a064..31e2c4f81 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -18,7 +18,6 @@ def initialize(object, order_group, name=nil, options={}) @options = options @null_object = false @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) } - @subscribers = options.fetch(:subscribers, []) end # @private @@ -153,7 +152,7 @@ def record_message_received(message, *args, &block) def message_received(message, *args, &block) record_message_received message, *args, &block - @subscribers.each do |subscriber| + RSpec::Mocks.space.any_instance_recorders_from_ancestry_of(object).each do |subscriber| subscriber.notify_received_message(object, message, args, block) end diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index f6c6b08d4..231c896bd 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -21,6 +21,10 @@ def register_constant_mutator(mutator) raise_lifecycle_message end + def any_instance_recorders_from_ancestry_of(object) + raise_lifecycle_message + end + def reset_all end @@ -110,14 +114,20 @@ def registered?(object) proxies.has_key?(id_for object) end - private - def any_instance_recorders_from_ancestry_of(object) + # Optimization: `any_instance` is a feature we generally + # recommend not using, so we can often early exit here + # without doing an O(N) linear search over the number of + # ancestors in the object's class hierarchy. + return [] if any_instance_recorders.empty? + object.class.ancestors.map do |klass| any_instance_recorders[klass.__id__] end.compact end + private + # We don't want to depend on the stdlib ourselves, but if the user is # using threads then a Mutex will be available to us. If not, we don't # need to synchronize anyway. @@ -144,18 +154,9 @@ def proxy_not_found_for(id, object) end else if RSpec::Mocks.configuration.verify_partial_doubles? - VerifyingPartialDoubleProxy.new( - object, - @expectation_ordering, - :subscribers => any_instance_recorders_from_ancestry_of(object) - ) + VerifyingPartialDoubleProxy.new(object, @expectation_ordering) else - PartialDoubleProxy.new( - object, - @expectation_ordering, - nil, - :subscribers => any_instance_recorders_from_ancestry_of(object) - ) + PartialDoubleProxy.new(object, @expectation_ordering) end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index b3a4a4923..99b18a8ec 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -86,8 +86,8 @@ def visibility_for(method_name) class VerifyingPartialDoubleProxy < PartialDoubleProxy include VerifyingProxyMethods - def initialize(object, expectation_ordering, options={}) - super(object, expectation_ordering, nil, options) + def initialize(object, expectation_ordering) + super(object, expectation_ordering) @doubled_module = DirectObjectReference.new(object) # A custom method double is required to pass through a way to lookup diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index e12aa2a1c..147880cb9 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -457,6 +457,15 @@ def inspect expect(instance.msg_2).to eq("b") end + it "properly notifies any instance recorders when they are created after the object's mock proxy" do + object = Object.new + allow(object).to receive(:bar) + expect_any_instance_of(Object).to receive(:foo).twice + allow(object).to receive(:foo).and_return(3) + expect(object.foo).to eq(3) + expect(object.foo).to eq(3) + end + context "with an expectation is set on a method which does not exist" do it "returns the expected value" do expect_any_instance_of(klass).to receive(:foo).and_return(1) From 30a175148625f46b3bc291430e68d1d752404e17 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 30 Apr 2014 09:06:13 -0700 Subject: [PATCH 0597/1179] Only fulfill any_instance expectations when args match. --- lib/rspec/mocks/any_instance/chain.rb | 11 ++++++++++ .../mocks/any_instance/message_chains.rb | 9 +++++++++ lib/rspec/mocks/any_instance/recorder.rb | 4 +++- spec/rspec/mocks/any_instance_spec.rb | 20 +++++++++++++++++++ .../matchers/receive_message_chain_spec.rb | 3 --- 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 9c52ff567..4bcc1748c 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -8,6 +8,7 @@ def initialize(recorder, *args, &block) @recorder = recorder @expectation_args = args @expectation_block = block + @argument_list_matcher = ArgumentListMatcher::MATCH_ALL end # @private @@ -63,6 +64,11 @@ def constrained_to_any_of?(*constraints) end end + # @private + def matches_args?(*args) + @argument_list_matcher.args_match?(*args) + end + # @private def expectation_fulfilled! @expectation_fulfilled = true @@ -73,6 +79,11 @@ def never super end + def with(*args, &block) + @argument_list_matcher = ArgumentListMatcher.new(*args) + super + end + private def negated? diff --git a/lib/rspec/mocks/any_instance/message_chains.rb b/lib/rspec/mocks/any_instance/message_chains.rb index 205169b8b..067096938 100644 --- a/lib/rspec/mocks/any_instance/message_chains.rb +++ b/lib/rspec/mocks/any_instance/message_chains.rb @@ -32,6 +32,15 @@ def has_expectation?(method_name) end end + # @private + def each_unfulfilled_expectation_matching(method_name, *args) + @chains_by_method_name[method_name].each do |chain| + if !chain.expectation_fulfilled? && chain.matches_args?(*args) + yield chain + end + end + end + # @private def all_expectations_fulfilled? @chains_by_method_name.all? do |method_name, chains| diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 1d49d3bb2..9dbf0221a 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -123,7 +123,9 @@ def already_observing?(method_name) # @private def notify_received_message(object, message, args, blk) - received_expected_message!(message) if message_chains.has_expectation?(message) + message_chains.each_unfulfilled_expectation_matching(message, *args) do |expectation| + expectation.expectation_fulfilled! + end end protected diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 147880cb9..b9390d60b 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -466,6 +466,26 @@ def inspect expect(object.foo).to eq(3) end + context "when argument matching is used and an instance has stubbed the message" do + it "fails on verify if the arguments do not match" do + expect_any_instance_of(klass).to receive(:foo).with(3) + instance = klass.new + allow(instance).to receive(:foo).and_return(2) + + expect(instance.foo(4)).to eq(2) + expect { verify_all }.to fail + end + + it "passes on verify if the arguments do match" do + expect_any_instance_of(klass).to receive(:foo).with(3) + instance = klass.new + allow(instance).to receive(:foo).and_return(2) + + expect(instance.foo(3)).to eq(2) + expect { verify_all }.not_to raise_error + end + end + context "with an expectation is set on a method which does not exist" do it "returns the expected value" do expect_any_instance_of(klass).to receive(:foo).and_return(1) diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index 3931ac62d..696a5d075 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -165,9 +165,6 @@ module RSpec::Mocks::Matchers expect_any_instance_of(Object).to receive_message_chain(:foo, :bar => 3) expect(o.foo.bar).to eq(3) - - # TODO: this shouldn't be necessary to satisfy the expectation, but is. - Object.new.foo.bar end it "passes when with expect_any_instance_of is used and the entire chain is called" do From 3b02aa62932a81665e848232a670af9f805b50c4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 30 Apr 2014 09:31:19 -0700 Subject: [PATCH 0598/1179] Don't allow a second instance to receive the message. This aligns with the normal `any_instance` behavior. --- lib/rspec/mocks/any_instance/recorder.rb | 8 ++++++++ spec/rspec/mocks/any_instance_spec.rb | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 9dbf0221a..500ced050 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -123,9 +123,17 @@ def already_observing?(method_name) # @private def notify_received_message(object, message, args, blk) + has_expectation = false + message_chains.each_unfulfilled_expectation_matching(message, *args) do |expectation| + has_expectation = true expectation.expectation_fulfilled! end + + if has_expectation + restore_method!(message) + mark_invoked!(message) + end end protected diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index b9390d60b..90cde2339 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -444,7 +444,7 @@ def inspect expect(subinstance.foo(2)).to eq(2) end - it "properly notifies any instance recorders at multiple levels of hierarchy" do + it "properly notifies any instance recorders at multiple levels of hierarchy when a directly stubbed object receives a message" do subclass = Class.new(klass) instance = subclass.new @@ -466,6 +466,20 @@ def inspect expect(object.foo).to eq(3) end + context "when an instance has been directly stubbed" do + it "fails when a second instance to receive the message" do + expect_any_instance_of(klass).to receive(:foo) + instance_1 = klass.new + + allow(instance_1).to receive(:foo).and_return(17) + expect(instance_1.foo).to eq(17) + + expect { + klass.new.foo + }.to fail_with(/has already been received/) + end + end + context "when argument matching is used and an instance has stubbed the message" do it "fails on verify if the arguments do not match" do expect_any_instance_of(klass).to receive(:foo).with(3) From 641bf87c63561beeb112d64d13c4be9729f2f5f7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 30 Apr 2014 09:35:46 -0700 Subject: [PATCH 0599/1179] Add changelog entry. --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index c156d544a..f95229185 100644 --- a/Changelog.md +++ b/Changelog.md @@ -41,6 +41,9 @@ Bug Fixes: already been stubbed. (Myron Marston) * Fix verified doubles so that their class name is included in failure messages. (Myron Marston) +* Fix `expect_any_instance_of` so that when the message is received + on an individual instance that has been directly stubbed, it still + satisfies the expectation. (Sam Phippen, Myron Marston) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) From 93b7b109186332512cb8bf22d6957798ed83a9ef Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 30 Apr 2014 21:32:09 -0700 Subject: [PATCH 0600/1179] Work around a few travis/bundler issues. --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0f7718476..15f51ed54 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,11 @@ # This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -before_install: "script/clone_all_rspec_repos" +before_install: + - "script/clone_all_rspec_repos" + # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 + - gem install bundler -v=1.5.3 + - alias bundle="bundle _1.5.3_" bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: @@ -22,4 +26,6 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx + # Until https://github.com/travis-ci/travis-ci/issues/2220 is fixed + - rvm: 2.1.0 fast_finish: true From b8a5b038bd6e61ade5d821e49510cdf6e0487d75 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 30 Apr 2014 21:34:32 -0700 Subject: [PATCH 0601/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 10 ++++++++-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f7718476..c48001c93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,11 @@ -# This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. +# This file was generated on 2014-04-30T21:34:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -before_install: "script/clone_all_rspec_repos" +before_install: + - "script/clone_all_rspec_repos" + # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 + - gem install bundler -v=1.5.3 + - alias bundle="bundle _1.5.3_" bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: @@ -22,4 +26,6 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx + # Until https://github.com/travis-ci/travis-ci/issues/2220 is fixed + - rvm: 2.1.0 fast_finish: true diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 9777ecfdd..535bb1616 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. +# This file was generated on 2014-04-30T21:34:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 759b543f9..b6c9a7e06 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. +# This file was generated on 2014-04-30T21:34:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index de3644218..0334780db 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-03-30T13:16:22-07:00 from the rspec-dev repo. +# This file was generated on 2014-04-30T21:34:22-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From 8b757c203037d345bf02044c8beb16a25e99d2ae Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 5 May 2014 21:32:26 -0700 Subject: [PATCH 0602/1179] Disallow `any_instance` for a method defined on a prepended module. It's very hard to fully support so we are choosing not to at this time. Closes #666. Closes #661. --- Changelog.md | 4 ++ lib/rspec/mocks/any_instance/recorder.rb | 18 +++++++++ spec/rspec/mocks/any_instance_spec.rb | 49 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/Changelog.md b/Changelog.md index f95229185..45b1e9eee 100644 --- a/Changelog.md +++ b/Changelog.md @@ -44,6 +44,10 @@ Bug Fixes: * Fix `expect_any_instance_of` so that when the message is received on an individual instance that has been directly stubbed, it still satisfies the expectation. (Sam Phippen, Myron Marston) +* Explicitly disallow using `any_instance` to mock or stub a method + that is defined on a module prepended onto the class. This triggered + `SystemStackError` before and is very hard to support so we are not + supporting it at this time. (Myron Marston) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 500ced050..3a018ea6a 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -214,6 +214,8 @@ def public_protected_or_private_method_defined?(method_name) end def observe!(method_name) + allow_no_prepended_module_definition_of(method_name) + if RSpec::Mocks.configuration.verify_partial_doubles? unless public_protected_or_private_method_defined?(method_name) raise MockExpectationError, @@ -241,6 +243,22 @@ def mark_invoked!(method_name) end end + if Support::RubyFeatures.module_prepends_supported? + def allow_no_prepended_module_definition_of(method_name) + prepended_modules = @klass.ancestors.take_while { |mod| !(Class === mod) } + problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) } + return unless problem_mod + + raise RSpec::Mocks::MockExpectationError, + "Using `any_instance` to stub a method (#{method_name}) that has been " + + "defined on a prepended module (#{problem_mod}) is not supported." + end + else + def allow_no_prepended_module_definition_of(method_name) + # nothing to do; prepends aren't supported on this version of ruby + end + end + end end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 90cde2339..1d23f1487 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -113,6 +113,38 @@ def private_method; :private_method_return_value; end end end + context "when the class has a prepended module", :if => Support::RubyFeatures.module_prepends_supported? do + it 'allows stubbing a method that is not defined on the prepended module' do + klass.class_eval { prepend Module.new { def other; end } } + allow_any_instance_of(klass).to receive(:foo).and_return(45) + + expect(klass.new.foo).to eq(45) + end + + it 'prevents stubbing a method that is defined on the prepended module' do + klass.class_eval { prepend Module.new { def foo; end } } + + expect { + allow_any_instance_of(klass).to receive(:foo).and_return(45) + }.to fail_with(/prepended module/) + end + + it 'allows stubbing a chain starting with a method that is not defined on the prepended module' do + klass.class_eval { prepend Module.new { def other; end } } + allow_any_instance_of(klass).to receive_message_chain(:foo, :bar).and_return(45) + + expect(klass.new.foo.bar).to eq(45) + end + + it 'prevents stubbing a chain starting with a method that is defined on the prepended module' do + klass.class_eval { prepend Module.new { def foo; end } } + + expect { + allow_any_instance_of(klass).to receive_message_chain(:foo, :bar).and_return(45) + }.to fail_with(/prepended module/) + end + end + context "with argument matching" do before do allow_any_instance_of(klass).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) @@ -466,6 +498,23 @@ def inspect expect(object.foo).to eq(3) end + context "when the class has a prepended module", :if => Support::RubyFeatures.module_prepends_supported? do + it 'allows mocking a method that is not defined on the prepended module' do + klass.class_eval { prepend Module.new { def other; end } } + expect_any_instance_of(klass).to receive(:foo).and_return(45) + + expect(klass.new.foo).to eq(45) + end + + it 'prevents mocking a method that is defined on the prepended module' do + klass.class_eval { prepend Module.new { def foo; end } } + + expect { + expect_any_instance_of(klass).to receive(:foo).and_return(45) + }.to fail_with(/prepended module/) + end + end + context "when an instance has been directly stubbed" do it "fails when a second instance to receive the message" do expect_any_instance_of(klass).to receive(:foo) From 9aaad74e731ec469bfce1ba856d54acbae983e61 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 5 May 2014 21:50:24 -0700 Subject: [PATCH 0603/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 4 +--- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index c48001c93..cfebe3d8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-04-30T21:34:22-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-05T21:50:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: @@ -26,6 +26,4 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx - # Until https://github.com/travis-ci/travis-ci/issues/2220 is fixed - - rvm: 2.1.0 fast_finish: true diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 535bb1616..df486357d 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-04-30T21:34:22-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-05T21:50:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index b6c9a7e06..c0e9d61ca 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-04-30T21:34:22-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-05T21:50:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index 0334780db..b62029831 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-04-30T21:34:22-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-05T21:50:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From 623b7a6b8a8fa7da08afedefebacee3249df06ae Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 May 2014 09:46:45 -0700 Subject: [PATCH 0604/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 3 ++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index cfebe3d8e..df9c56b6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-05-05T21:50:14-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-09T09:46:37-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: @@ -15,6 +15,7 @@ rvm: - 2.0.0 - 2.1.0 - 2.1.1 + - 2.1.2 - ruby-head - ree - jruby-18mode diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index df486357d..ad653c27d 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-05-05T21:50:14-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-09T09:46:37-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index c0e9d61ca..514255811 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-05-05T21:50:14-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-09T09:46:37-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index b62029831..6eb09fb97 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-05-05T21:50:14-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-09T09:46:37-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From c8ca829537ca136a7fc67e5d71a5d180360c1ebf Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 18 May 2014 15:50:52 -0700 Subject: [PATCH 0605/1179] Forwardport release notes from 2.99.0.rc1 [ci skip] --- Changelog.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 45b1e9eee..b0b391bf3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -117,7 +117,7 @@ Bug Fixes: behavior. (Maurício Linhares) ### 3.0.0.beta1 / 2013-11-07 -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v3.0.0.beta1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.rc1...v3.0.0.beta1) Breaking Changes for 3.0.0: @@ -180,6 +180,15 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.0.rc1 / 2014-05-18 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta2...v2.99.0.rc1) + +Deprecations: + +* Deprecate `RSpec::Mocks::TestDouble.extend_onto`. (Myron Marston) +* Deprecate `RSpec::Mocks::ConstantStubber`. (Jon Rowe) +* Deprecate `Marshal.dump` monkey-patch without opt-in. (Xavier Shay) + ### 2.99.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v2.99.0.beta2) @@ -221,7 +230,7 @@ Enhancements: blocks. (Sam Phippen) ### 2.14.6 / 2014-02-20 -[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.5...v2.14.6) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.5...v2.14.6) Bug Fixes: From 2596cee0bb9ed86a9b172693646cca7f5e3e029c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 18 May 2014 15:56:12 -0700 Subject: [PATCH 0606/1179] Prep changelog for 3.0.0.rc1 [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index b0b391bf3..d33f3ad4f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.0.0.rc1 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...master) +### 3.0.0.rc1 / 2014-05-18 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...v3.0.0.rc1) Breaking Changes for 3.0.0: From 9e018ebeb86284b810f2d7cdcb44eaf572dba5a1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 18 May 2014 15:58:54 -0700 Subject: [PATCH 0607/1179] Release 3.0.0.rc1 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index d3099aeb7..181fc1bb7 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.0.0.beta2' + STRING = '3.0.0.rc1' end end end From ee7ccd458ab1e579b61771fabe267ad6515b1742 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 19 May 2014 17:44:26 -0700 Subject: [PATCH 0608/1179] Fix prepended module detection. On ruby 2.0, when a module was extended onto itself, we wrongly considered it to be a prepended module. --- Changelog.md | 8 ++++++++ lib/rspec/mocks/proxy.rb | 2 +- spec/rspec/mocks/stub_spec.rb | 11 +++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d33f3ad4f..09a09706a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### 3.0.0 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.rc1...master) + +Bug Fixes: + +* Fix module prepend detection to work properly on ruby 2.0 for a case + where a module is extended onto itself. (Myron Marston) + ### 3.0.0.rc1 / 2014-05-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...v3.0.0.rc1) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 31e2c4f81..8b7ca77a6 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -206,7 +206,7 @@ def prepended_modules_of_singleton_class @prepended_modules_of_singleton_class ||= begin singleton_class = @object.singleton_class singleton_class.ancestors.take_while do |mod| - !(Class === mod) + !(Class === mod || @object.equal?(mod)) end end end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 11b36080d..630072310 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -126,6 +126,17 @@ def object.value; :original; end }.not_to change { object.singleton_class.ancestors } end + it 'does not unnecessarily prepend a module when stubbing a method on a module extended onto itself' do + mod = Module.new do + extend self + def foo; :bar; end + end + + expect { + allow(mod).to receive(:foo) + }.not_to change { mod.singleton_class.ancestors } + end + it 'reuses our prepend module so as not to keep mutating the ancestors' do object = Object.new def object.value; :original; end From b8d4a252c23d914eadcde07c6d04c8ef73bee5e0 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 20 May 2014 15:40:26 +1000 Subject: [PATCH 0609/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 9 ++++++--- script/clone_all_rspec_repos | 2 +- script/functions.sh | 4 ++-- script/run_build | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index df9c56b6c..dcb3ecc28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ -# This file was generated on 2014-05-09T09:46:37-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-20T15:40:07+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: - "script/clone_all_rspec_repos" # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 - - gem install bundler -v=1.5.3 - - alias bundle="bundle _1.5.3_" + # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it + - if [ -z "$JRUBY_OPTS" ]; then gem install bundler -v=1.5.3 && alias bundle="bundle _1.5.3_"; fi bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: @@ -23,6 +23,9 @@ rvm: - jruby-head - rbx matrix: + include: + - rvm: jruby + env: JRUBY_OPTS='--2.0' allow_failures: - rvm: jruby-head - rvm: ruby-head diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index ad653c27d..66b0cf2a0 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-05-09T09:46:37-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-20T15:40:07+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 514255811..0640d4869 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,8 +1,8 @@ -# This file was generated on 2014-05-09T09:46:37-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-20T15:40:07+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html -export JRUBY_OPTS='-X-C' # disable JIT since these processes are so short lived +export JRUBY_OPTS="${JRUBY_OPTS} -X-C" # disable JIT since these processes are so short lived SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` diff --git a/script/run_build b/script/run_build index 6eb09fb97..47fb14c0b 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-05-09T09:46:37-07:00 from the rspec-dev repo. +# This file was generated on 2014-05-20T15:40:07+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From d4885eb63c71b2b9516428fb58e39ce09dc768d2 Mon Sep 17 00:00:00 2001 From: Tom Ward Date: Wed, 21 May 2014 10:44:53 +0100 Subject: [PATCH 0610/1179] Fix link to dynamic-classes feature --- features/verifying_doubles/instance_doubles.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifying_doubles/instance_doubles.feature b/features/verifying_doubles/instance_doubles.feature index adc12ade4..89d704f40 100644 --- a/features/verifying_doubles/instance_doubles.feature +++ b/features/verifying_doubles/instance_doubles.feature @@ -7,7 +7,7 @@ Feature: Using an instance double arguments is correct. For methods handled by `method_missing`, see [dynamic - objects](./dynamic-objects). + classes](./dynamic-classes). Background: Given a file named "app/models/user.rb" with: From e62d011dcd79115870184082d1cd049a6e1712f4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 22 May 2014 10:22:02 -0700 Subject: [PATCH 0611/1179] Fix `config.transfer_nested_constants = true`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We shouldn’t give users an error when they stub a constant that is not a class or module. Fixes #677. --- Changelog.md | 3 +++ lib/rspec/mocks/mutate_const.rb | 13 ++++++++----- spec/rspec/mocks/mutate_const_spec.rb | 14 +++++++++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 09a09706a..4702ef15a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,9 @@ Bug Fixes: * Fix module prepend detection to work properly on ruby 2.0 for a case where a module is extended onto itself. (Myron Marston) +* Fix `config.transfer_nested_constants = true` so that you don't + erroneously get errors when stubbing a constant that is not a module + or a class. (Myron Marston) ### 3.0.0.rc1 / 2014-05-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...v3.0.0.rc1) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index b5c640202..bb5ea7898 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -174,10 +174,7 @@ def self.stub(constant_name, value, options = {}) UndefinedConstantSetter end - mutate(mutator.new(constant_name, value, options.fetch( - :transfer_nested_constants, - RSpec::Mocks.configuration.transfer_nested_constants? - ))) + mutate(mutator.new(constant_name, value, options[:transfer_nested_constants])) value end @@ -289,7 +286,7 @@ def transfer_nested_constants(constants) end def verify_constants_to_transfer! - return [] unless @transfer_nested_constants + return [] unless should_transfer_nested_constants? { @original_value => "the original value", @mutated_value => "the stubbed value" }.each do |value, description| unless value.respond_to?(:constants) @@ -317,6 +314,12 @@ def verify_constants_to_transfer! constants_defined_on(@original_value) end end + + def should_transfer_nested_constants? + return true if @transfer_nested_constants + return false unless RSpec::Mocks.configuration.transfer_nested_constants? + @original_value.respond_to?(:constants) && @mutated_value.respond_to?(:constants) + end end # Sets an undefined constant for the duration of an example. diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 9bbb51978..c737be26d 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -325,8 +325,20 @@ def change_const_value_to(value) expect(stub::N).to eq(:n) expect(stub::Nested).to be(tc_nested) end - end + context "when stubbing a constant that is not a module or a class" do + it 'does not attempt to transfer constants' do + stub_const("TOP_LEVEL_VALUE_CONST", 4) + expect(TOP_LEVEL_VALUE_CONST).to eq(4) + end + + it 'still raises an error when the `:transfer_nested_constants` option is provided' do + expect { + stub_const("TOP_LEVEL_VALUE_CONST", 4, :transfer_nested_constants => true) + }.to raise_error(/cannot transfer nested constant/i) + end + end + end end context 'for a loaded nested constant' do From 06a01962d4d1bb48b6fd148dce421101b59f13aa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 22 May 2014 14:57:26 -0700 Subject: [PATCH 0612/1179] Remove transferred nested constants on reset. Fixes #678. --- Changelog.md | 2 ++ lib/rspec/mocks/mutate_const.rb | 12 ++++++++---- spec/rspec/mocks/mutate_const_spec.rb | 9 +++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4702ef15a..5cd535afe 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Bug Fixes: * Fix module prepend detection to work properly on ruby 2.0 for a case where a module is extended onto itself. (Myron Marston) +* Fix `transfer_nested_constants` option so that transferred constants + get properly reset at the end of the example. (Myron Marston) * Fix `config.transfer_nested_constants = true` so that you don't erroneously get errors when stubbing a constant that is not a module or a class. (Myron Marston) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index bb5ea7898..b5ddabdf0 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -258,12 +258,12 @@ def mutate @context = recursive_const_get(@context_parts.join('::')) @original_value = get_const_defined_on(@context, @const_name) - constants_to_transfer = verify_constants_to_transfer! + @constants_to_transfer = verify_constants_to_transfer! @context.__send__(:remove_const, @const_name) @context.const_set(@const_name, @mutated_value) - transfer_nested_constants(constants_to_transfer) + transfer_nested_constants end def to_constant @@ -275,12 +275,16 @@ def to_constant end def reset + Array(@constants_to_transfer).each do |const| + @mutated_value.__send__(:remove_const, const) + end + @context.__send__(:remove_const, @const_name) @context.const_set(@const_name, @original_value) end - def transfer_nested_constants(constants) - constants.each do |const| + def transfer_nested_constants + @constants_to_transfer.each do |const| @mutated_value.const_set(const, get_const_defined_on(original_value, const)) end end diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index c737be26d..258305083 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -241,6 +241,15 @@ def change_const_value_to(value) expect(stub::Nested).to be(tc_nested) end + it 'removes the transferred constants on reset' do + stub = Module.new + stub_const("TestClass", stub, :transfer_nested_constants => true) + + expect { + reset_all + }.to change { stub.constants }.to([]) + end + it 'does not transfer nested constants that are inherited from a superclass' do stub = Module.new stub_const("TestSubClass", stub, :transfer_nested_constants => true) From 06f08762ab367104e96b3a57e9b8d44da7447ce8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 22 May 2014 18:09:16 -0700 Subject: [PATCH 0613/1179] Address ruby warning. warning: instance variable @constants_to_transfer not initialized --- lib/rspec/mocks/mutate_const.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index b5ddabdf0..90a9fbf11 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -254,6 +254,11 @@ def reset # # @private class DefinedConstantReplacer < BaseMutator + def initialize(*args) + super + @constants_to_transfer = [] + end + def mutate @context = recursive_const_get(@context_parts.join('::')) @original_value = get_const_defined_on(@context, @const_name) @@ -275,7 +280,7 @@ def to_constant end def reset - Array(@constants_to_transfer).each do |const| + @constants_to_transfer.each do |const| @mutated_value.__send__(:remove_const, const) end From 47ae1a2f03a03680f47998c188d90be95db0edbd Mon Sep 17 00:00:00 2001 From: Pete Higgins Date: Sun, 25 May 2014 19:53:25 -0700 Subject: [PATCH 0614/1179] Cleanup cruft in double spec. --- spec/rspec/mocks/double_spec.rb | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 3df39cd85..37d9aa7c5 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -110,25 +110,23 @@ module Mocks it "reports line number of expectation of unreceived message" do expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) - begin + expect { verify @double - violated - rescue RSpec::Mocks::MockExpectationError => e + }.to raise_error(RSpec::Mocks::MockExpectationError) { |e| # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) - end + } end it "reports line number of expectation of unreceived message after a message expecation after similar stub" do allow(@double).to receive(:wont_happen) expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) - begin + expect { verify @double - violated - rescue RSpec::Mocks::MockExpectationError => e + }.to raise_error(RSpec::Mocks::MockExpectationError) { |e| # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) - end + } end it "passes when not receiving message specified as not to be received" do @@ -167,7 +165,6 @@ module Mocks expect(@double).not_to receive(:not_expected) expect { @double.not_expected - violated }.to raise_error( RSpec::Mocks::MockExpectationError, %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| @@ -178,7 +175,6 @@ module Mocks expect(@double).not_to receive(:not_expected).with("unexpected text") expect { @double.not_expected("unexpected text") - violated }.to raise_error( RSpec::Mocks::MockExpectationError, %Q|(Double "test double").not_expected("unexpected text")\n expected: 0 times with arguments: ("unexpected text")\n received: 1 time with arguments: ("unexpected text")| @@ -189,7 +185,6 @@ module Mocks expect(@double).not_to receive(:not_expected).with(["do not want"]) expect { @double.not_expected(["do not want"]) - violated }.to raise_error( RSpec::Mocks::MockExpectationError, %Q|(Double "test double").not_expected(["do not want"])\n expected: 0 times with arguments: (["do not want"])\n received: 1 time with arguments: (["do not want"])| @@ -256,7 +251,6 @@ module Mocks expect(@double).to receive(:something).with("a","b","c").and_return("booh") expect { @double.something("a","d","c") - violated }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") end @@ -304,7 +298,6 @@ def @double.method_with_default_argument(arg={}); end it "fails if unexpected method called" do expect { @double.something("a","b","c") - violated }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received unexpected message :something with (\"a\", \"b\", \"c\")") end @@ -418,13 +411,12 @@ def initialize(amount, units) it "raises a given instance of an exception with arguments other than the standard 'message'" do expect(@double).to receive(:something).and_raise(OutOfGas.new(2, :oz)) - begin + expect { @double.something - fail "OutOfGas was not raised" - rescue OutOfGas => e + }.to raise_error(OutOfGas) { |e| expect(e.amount).to eq 2 expect(e.units).to eq :oz - end + } end it "does not raise when told to if args dont match" do From 0b1249011ad975ab80b6798dc23234e57543f394 Mon Sep 17 00:00:00 2001 From: Cezar Halmagean Date: Mon, 26 May 2014 22:48:58 +0300 Subject: [PATCH 0615/1179] Display warning for ruby 2.0.0p195 about the TypeError bug --- lib/rspec/mocks/instance_method_stasher.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index d540dac14..16c5c9951 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -73,8 +73,8 @@ def restore end end - if RUBY_DESCRIPTION.include?('2.0.0p247') - # ruby 2.0.0-p247 has a bug that we can't work around :(. + if RUBY_DESCRIPTION.include?('2.0.0p247') || RUBY_DESCRIPTION.include?('2.0.0p195') + # ruby 2.0.0-p247 and 2.0.0-p195 both have a bug that we can't work around :(. # https://bugs.ruby-lang.org/issues/8686 def handle_restoration_failures yield From ce8a4f4eb493c81a1432de09e928aa9aac47d1b9 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 27 May 2014 10:41:52 +1000 Subject: [PATCH 0616/1179] changelog for #684 and tidy up message --- Changelog.md | 6 ++++++ lib/rspec/mocks/instance_method_stasher.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 5cd535afe..d0afe9d2a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,12 @@ Bug Fixes: erroneously get errors when stubbing a constant that is not a module or a class. (Myron Marston) +Enhancements: + +* Display warning when encountering `TypeError` during instance method + staging on 2.0.0-p195, suffers from https://bugs.ruby-lang.org/issues/8686 + too. (Cezar Halmagean). + ### 3.0.0.rc1 / 2014-05-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...v3.0.0.rc1) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index 16c5c9951..b7e6446d3 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -81,7 +81,7 @@ def handle_restoration_failures rescue TypeError RSpec.warn_with( "RSpec failed to properly restore a partial double (#{@object.inspect}) " + - "to its original state due to a known bug in MRI 2.0.0-p247 " + + "to its original state due to a known bug in MRI 2.0.0-p195 & p247 " + "(https://bugs.ruby-lang.org/issues/8686). This object may remain " + "screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.", :call_site => nil, :use_spec_location_as_call_site => true From 6176d177121cdb4b83a31fac3b647ff89148a9b5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 26 May 2014 22:04:51 -0700 Subject: [PATCH 0617/1179] Remove watchr file. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I don’t think any of use watcher. --- specs.watchr | 57 ---------------------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 specs.watchr diff --git a/specs.watchr b/specs.watchr deleted file mode 100644 index ad559afcc..000000000 --- a/specs.watchr +++ /dev/null @@ -1,57 +0,0 @@ -# Run me with: -# -# $ watchr specs.watchr - -# -------------------------------------------------- -# Convenience Methods -# -------------------------------------------------- -def all_test_files - Dir['spec/**/*_spec.rb'] -end - -def run_test_matching(thing_to_match) - matches = all_test_files.grep(/#{thing_to_match}/i) - if matches.empty? - puts "Sorry, thanks for playing, but there were no matches for #{thing_to_match}" - else - run matches.join(' ') - end -end - -def run(files_to_run) - puts("Running: #{files_to_run}") - system("clear;rspec -cfs #{files_to_run}") - no_int_for_you -end - -def run_all - run(all_test_files.join(' ')) -end - -# -------------------------------------------------- -# Watchr Rules -# -------------------------------------------------- -watch('^spec/(.*)_spec\.rb') { run_all } -watch('^lib/(.*)\.rb') { run_all } - -# -------------------------------------------------- -# Signal Handling -# -------------------------------------------------- - -def no_int_for_you - @sent_an_int = nil -end - -Signal.trap 'INT' do - if @sent_an_int then - puts " A second INT? Ok, I get the message. Shutting down now." - exit - else - puts " Did you just send me an INT? Ugh. I'll quit for real if you do it again." - @sent_an_int = true - Kernel.sleep 1.5 - run_all - end -end - -# vim:ft=ruby From fb0c2d6d3f288c9ccf07bf3482b219f006cbaa05 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 2 May 2014 17:47:48 -0700 Subject: [PATCH 0618/1179] Refactor: use a local variable rather than re-fetching the recorder. --- lib/rspec/mocks/any_instance/recorder.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 3a018ea6a..17c91efc0 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -226,18 +226,18 @@ def observe!(method_name) stop_observing!(method_name) if already_observing?(method_name) @observed_methods << method_name backup_method!(method_name) + recorder = self @klass.__send__(:define_method, method_name) do |*args, &blk| - klass = ::RSpec::Support.method_handle_for(self, method_name).owner - ::RSpec::Mocks.space.any_instance_recorder_for(klass).playback!(self, method_name) + recorder.playback!(self, method_name) self.__send__(method_name, *args, &blk) end end def mark_invoked!(method_name) backup_method!(method_name) + recorder = self @klass.__send__(:define_method, method_name) do |*args, &blk| - klass = ::RSpec::Support.method_handle_for(self, method_name).owner - invoked_instance = ::RSpec::Mocks.space.any_instance_recorder_for(klass).instance_that_received(method_name) + invoked_instance = recorder.instance_that_received(method_name) inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>" raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{inspect} but has already been received by #{invoked_instance}" end From 1ffe304afe5a9ae0541e29c275908be8b8d3c204 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 29 May 2014 12:00:23 -0700 Subject: [PATCH 0619/1179] =?UTF-8?q?Don=E2=80=99t=20create=20unnecessary?= =?UTF-8?q?=20`any=5Finstance`=20recorders.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to #687. --- lib/rspec/mocks/proxy.rb | 5 ++++- lib/rspec/mocks/space.rb | 3 ++- spec/rspec/mocks/partial_double_spec.rb | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 8b7ca77a6..8cb693e2b 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -301,7 +301,10 @@ def reset private def any_instance_class_recorder_observing_method?(klass, method_name) - return true if ::RSpec::Mocks.space.any_instance_recorder_for(klass).already_observing?(method_name) + only_return_existing = true + recorder = ::RSpec::Mocks.space.any_instance_recorder_for(klass, only_return_existing) + return true if recorder && recorder.already_observing?(method_name) + superklass = klass.superclass return false if superklass.nil? any_instance_class_recorder_observing_method?(superklass, method_name) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 231c896bd..dbc2e3d59 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -84,10 +84,11 @@ def constant_mutator_for(name) @constant_mutators.find { |m| m.full_constant_name == name } end - def any_instance_recorder_for(klass) + def any_instance_recorder_for(klass, only_return_existing = false) any_instance_mutex.synchronize do id = klass.__id__ any_instance_recorders.fetch(id) do + return nil if only_return_existing any_instance_recorder_not_found_for(id, klass) end end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 8622a82ae..11226e1c3 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -3,6 +3,12 @@ module Mocks describe "A partial double" do let(:object) { Object.new } + it 'does not create an any_instance recorder when a message is allowed' do + expect { + allow(object).to receive(:foo) + }.not_to change { RSpec::Mocks.space.any_instance_recorders }.from({}) + end + it "names the class in the failure message" do expect(object).to receive(:foo) expect do From c3c01c68a6c6a046d19b69811451a516d0c6ddad Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 29 May 2014 12:03:02 -0700 Subject: [PATCH 0620/1179] Only broadcast to any_instance_recorders for partial doubles. For pure doubles this is unneeded. --- lib/rspec/mocks/proxy.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 8cb693e2b..663615e7d 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -152,10 +152,6 @@ def record_message_received(message, *args, &block) def message_received(message, *args, &block) record_message_received message, *args, &block - RSpec::Mocks.space.any_instance_recorders_from_ancestry_of(object).each do |subscriber| - subscriber.notify_received_message(object, message, args, block) - end - expectation = find_matching_expectation(message, *args) stub = find_matching_method_stub(message, *args) @@ -298,6 +294,13 @@ def reset super end + def message_received(message, *args, &block) + RSpec::Mocks.space.any_instance_recorders_from_ancestry_of(object).each do |subscriber| + subscriber.notify_received_message(object, message, args, block) + end + super + end + private def any_instance_class_recorder_observing_method?(klass, method_name) From 829b0de5cd3b805412f62e690689c7d91361f2c7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 29 May 2014 18:34:14 -0700 Subject: [PATCH 0621/1179] Add spec to prevent future regressions of #687. Fixes #687. --- Changelog.md | 2 ++ spec/rspec/mocks/double_spec.rb | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/Changelog.md b/Changelog.md index d0afe9d2a..95449498d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,8 @@ Bug Fixes: * Fix `config.transfer_nested_constants = true` so that you don't erroneously get errors when stubbing a constant that is not a module or a class. (Myron Marston) +* Fix regression that caused `double(:class => SomeClass)` to later + trigger infinite recursion. (Myron Marston) Enhancements: diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 37d9aa7c5..e61ed760d 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -48,6 +48,17 @@ module Mocks expect(dbl.foo = "bar").to eq("bar") end + it 'allows `class` to be stubbed even when `any_instance` has already been used' do + # See https://github.com/rspec/rspec-mocks/issues/687 + # The infinite recursion code path was only triggered when there were + # active any instance recorders in the current example, so we make one here. + allow_any_instance_of(Object).to receive(:bar).and_return(2) + + dbl = double(:foo => 1, :class => String) + expect(dbl.foo).to eq(1) + expect(dbl.class).to eq(String) + end + it 'allows `send` to be stubbed' do dbl = double allow(dbl).to receive(:send).and_return("received") From 26bc2c560882e9b1ff297424421adae26f8bd233 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 29 May 2014 23:25:06 -0700 Subject: [PATCH 0622/1179] Access the ancestors through the singleton class. This always works even if `class` has been stubbed. --- lib/rspec/mocks/space.rb | 4 +++- spec/rspec/mocks/partial_double_spec.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index dbc2e3d59..b9a649b43 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -122,7 +122,9 @@ def any_instance_recorders_from_ancestry_of(object) # ancestors in the object's class hierarchy. return [] if any_instance_recorders.empty? - object.class.ancestors.map do |klass| + # We access the ancestors through the singleton class, to avoid calling + # `class` in case `class` has been stubbed. + (class << object; ancestors; end).map do |klass| any_instance_recorders[klass.__id__] end.compact end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 11226e1c3..d6cccbc00 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -29,6 +29,20 @@ module Mocks object.blah end + it 'allows `class` to be stubbed even when `any_instance` has already been used' do + # See https://github.com/rspec/rspec-mocks/issues/687 + # The infinite recursion code path was only triggered when there were + # active any instance recorders in the current example, so we make one here. + allow_any_instance_of(Object).to receive(:bar).and_return(2) + + expect(object.class).not_to eq(String) + allow(object).to receive_messages(:foo => 1, :class => String) + + expect(object.foo).to eq(1) + expect(object.class).to eq(String) + expect(object.bar).to eq(2) + end + it "can disallow messages from being received" do expect(object).not_to receive(:fuhbar) expect { From 50478659feda9bc0d3fd3ef23c26a0f93a40bbfb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 30 May 2014 15:51:34 -0700 Subject: [PATCH 0623/1179] Fix `have_received(...).with(...).ordered`. The arguments were not being considered. For #689. --- Changelog.md | 2 ++ lib/rspec/mocks/proxy.rb | 6 +++--- .../mocks/matchers/have_received_spec.rb | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 95449498d..bbf9d7da2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,8 @@ Bug Fixes: or a class. (Myron Marston) * Fix regression that caused `double(:class => SomeClass)` to later trigger infinite recursion. (Myron Marston) +* Fix bug in `have_received(...).with(...).ordered` where it was not + taking the args into account when checking the order. (Myron Marston) Enhancements: diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 663615e7d..2219b88e1 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -2,9 +2,9 @@ module RSpec module Mocks # @private class Proxy - SpecificMessage = Struct.new(:object,:message) do + SpecificMessage = Struct.new(:object, :message, :args) do def ==(expectation) - expectation.orig_object == object && expectation.message == message + expectation.orig_object == object && expectation.matches?(message, *args) end end @@ -144,7 +144,7 @@ def has_negative_expectation?(message) # @private def record_message_received(message, *args, &block) - @order_group.invoked SpecificMessage.new(object, message) + @order_group.invoked SpecificMessage.new(object, message, args) @messages_received << [message, args, block] end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index a1c1560f2..90f179bcd 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -273,6 +273,25 @@ module Mocks expect(dbl).to have_received(:two).ordered }.to raise_error(/received :two out of order/m) end + + context "when used with `with`" do + before do + dbl.one(1) + dbl.one(2) + end + + it "passes when the order lines up" do + expect(dbl).to have_received(:one).with(1).ordered + expect(dbl).to have_received(:one).with(2).ordered + end + + it "fails when the order is not matched" do + expect { + expect(dbl).to have_received(:one).with(2).ordered + expect(dbl).to have_received(:one).with(1).ordered + }.to fail_with(/received :one out of order/m) + end + end end end From 15f90d92e33d6843685eda206876e79b2bef41d1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 30 May 2014 15:55:51 -0700 Subject: [PATCH 0624/1179] Fix bug in `have_received(...).ordered`. It worked properly when `as_null_object` or simple stubs (via `dbl(stub_hash)` or `allow(...).to receive_messages`) were used, but when an individual method was stubbed to allow it to be spied on, it was registerd as one of the expectations in the order group. When the first of multiple `have_received(...).ordered` calls were made, it would fail since the 2nd `have_received(...).ordered` call hadn't yet been made but the invocation of both methods had already happened. By ignoring stubs, it allows each `have_received` expectation to consider only the messages that have been made part of the order group (by the `ordered` call) so far. Fixes #689 --- Changelog.md | 2 ++ lib/rspec/mocks/message_expectation.rb | 6 ++--- lib/rspec/mocks/method_double.rb | 4 ++-- .../mocks/matchers/have_received_spec.rb | 22 +++++++++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index bbf9d7da2..20f908433 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,8 @@ Bug Fixes: trigger infinite recursion. (Myron Marston) * Fix bug in `have_received(...).with(...).ordered` where it was not taking the args into account when checking the order. (Myron Marston) +* Fix bug in `have_received(...).ordered` where it was wrongly + considering stubs when checking the order. (Myron Marston) Enhancements: diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 4047ba9c0..8b21bce11 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -47,7 +47,7 @@ class MessageExpectation # @private def initialize(error_generator, expectation_ordering, expected_from, method_double, - expected_received_count=1, opts={}, &implementation_block) + type=:expectation, opts={}, &implementation_block) @error_generator = error_generator @error_generator.opts = opts @expected_from = expected_from @@ -55,10 +55,10 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @orig_object = @method_double.object @message = @method_double.method_name @actual_received_count = 0 - @expected_received_count = expected_received_count + @expected_received_count = type == :expectation ? 1 : :any @argument_list_matcher = ArgumentListMatcher::MATCH_ALL @order_group = expectation_ordering - @order_group.register(self) + @order_group.register(self) unless type == :stub @ordered = false @at_least = @at_most = @exactly = nil @args_to_yield = [] diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 785423c4b..000e57797 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -134,7 +134,7 @@ def message_expectation_class def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation) configure_method expectation = message_expectation_class.new(error_generator, expectation_ordering, - expected_from, self, 1, opts, &implementation) + expected_from, self, :expectation, opts, &implementation) expectations << expectation expectation end @@ -149,7 +149,7 @@ def build_expectation(error_generator, expectation_ordering) def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation) configure_method stub = message_expectation_class.new(error_generator, expectation_ordering, expected_from, - self, :any, opts, &implementation) + self, :stub, opts, &implementation) stubs.unshift stub stub end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 90f179bcd..9e5e7c432 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -292,6 +292,28 @@ module Mocks }.to fail_with(/received :one out of order/m) end end + + context "when used on individually allowed messages" do + before do + allow(dbl).to receive(:foo) + allow(dbl).to receive(:bar) + + dbl.foo + dbl.bar + end + + it 'passes when the messages were received in order' do + expect(dbl).to have_received(:foo).ordered + expect(dbl).to have_received(:bar).ordered + end + + it 'fails when the messages are received out of order' do + expect { + expect(dbl).to have_received(:bar).ordered + expect(dbl).to have_received(:foo).ordered + }.to raise_error(/received :foo out of order/m) + end + end end end From c8343fb113b7f1f022e25ff5889f6b116e3fcdf0 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 24 May 2014 11:30:15 +1000 Subject: [PATCH 0625/1179] use format_args for expected in raise_expectation_error --- lib/rspec/mocks/error_generator.rb | 10 +++++++--- spec/rspec/mocks/matchers/receive_spec.rb | 8 ++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index d067fd8fe..74032da7e 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -102,11 +102,11 @@ def received_part_of_expectation_error(actual_received_count, *args) # @private def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) "expected: #{count_message(expected_received_count, expectation_count_type)}" + - method_call_args_description(argument_list_matcher.expected_args) + method_call_args_description(argument_list_matcher.expected_args, true) end # @private - def method_call_args_description(args) + def method_call_args_description(args, format = false) case args.first when ArgumentMatchers::AnyArgsMatcher return " with any arguments" @@ -115,7 +115,11 @@ def method_call_args_description(args) end if args.length > 0 - " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}" + if format + " with arguments: #{format_args *args}" + else + " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}" + end else "" end diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index c36e120f5..49a1e3504 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -46,6 +46,14 @@ module Mocks }.to raise_error(/received :foo with unexpected arguments/) end + it 'allows the caller to constrain the received arguments by matcher' do + wrapped.to receive(:foo).with an_instance_of Fixnum + expect { + receiver.foo(1.1) + }.to raise_error(/expected.*\(an instance of Fixnum\)/) + reset_all + end + it 'allows a `do...end` block implementation to be provided' do wrapped.to receive(:foo) do 4 From 22dadf4f119a1cc6b8d3c2430539cb0ee60c0e4e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 31 May 2014 12:21:54 +1000 Subject: [PATCH 0626/1179] specify received counts are correct and split expected/actual formatting so its correct --- Changelog.md | 2 ++ lib/rspec/mocks/error_generator.rb | 36 +++++++++++++++-------- spec/rspec/mocks/matchers/receive_spec.rb | 14 ++++++++- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Changelog.md b/Changelog.md index 20f908433..7f6497dc8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,8 @@ Bug Fixes: taking the args into account when checking the order. (Myron Marston) * Fix bug in `have_received(...).ordered` where it was wrongly considering stubs when checking the order. (Myron Marston) +* Message expectation matchers now show descriptions from argument + matchers when their expectations aren't met. (Jon Rowe) Enhancements: diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 74032da7e..0092c2ee6 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -96,33 +96,43 @@ def raise_expired_test_double_error # @private def received_part_of_expectation_error(actual_received_count, *args) "received: #{count_message(actual_received_count)}" + - method_call_args_description(args) + actual_method_call_args_description(actual_received_count, args) end # @private def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) "expected: #{count_message(expected_received_count, expectation_count_type)}" + - method_call_args_description(argument_list_matcher.expected_args, true) + expected_method_call_args_description(argument_list_matcher.expected_args) end # @private - def method_call_args_description(args, format = false) + def actual_method_call_args_description(count, args) + method_call_args_description(args) || + if count > 0 && args.length > 0 + " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}" + else + "" + end + end + + # @private + def expected_method_call_args_description(args) + method_call_args_description(args) || + if args.length > 0 + " with arguments: #{format_args(*args)}" + else + "" + end + end + + # @private + def method_call_args_description(args) case args.first when ArgumentMatchers::AnyArgsMatcher return " with any arguments" when ArgumentMatchers::NoArgsMatcher return " with no arguments" end - - if args.length > 0 - if format - " with arguments: #{format_args *args}" - else - " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}" - end - else - "" - end end # @private diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 49a1e3504..f44a591a4 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -51,7 +51,7 @@ module Mocks expect { receiver.foo(1.1) }.to raise_error(/expected.*\(an instance of Fixnum\)/) - reset_all + receiver.foo(1) end it 'allows a `do...end` block implementation to be provided' do @@ -289,6 +289,13 @@ def receiver.method_missing(*a); end # a poor man's stub... it_behaves_like "an expect syntax expectation", :allow_other_matchers do let(:receiver) { double } let(:wrapped) { expect(receiver) } + + it 'sets up a message expectation that formats argument matchers correctly' do + wrapped.to receive(:foo).with an_instance_of Fixnum + expect { verify_all }.to( + raise_error(/expected: 1 time with arguments: \(an instance of Fixnum\)\n\s+received: 0 times$/) + ) + end end it_behaves_like "resets partial mocks cleanly" do let(:target) { expect(object) } @@ -300,6 +307,11 @@ def receiver.method_missing(*a); end # a poor man's stub... let(:klass) { Class.new } let(:wrapped) { expect_any_instance_of(klass) } let(:receiver) { klass.new } + + it 'sets up a message expectation that formats argument matchers correctly' do + wrapped.to receive(:foo).with an_instance_of Fixnum + expect { verify_all }.to raise_error(/should have received the following message\(s\) but didn't/) + end end it_behaves_like "resets partial mocks of any instance cleanly" do let(:target) { expect_any_instance_of(klass) } From e9295f2ddc997af3e6638d567ccb660be8938f9c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 1 Jun 2014 16:42:31 -0700 Subject: [PATCH 0627/1179] Forwardport 2.99.0 release notes. [ci skip] --- Changelog.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 7f6497dc8..d8653994b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -144,7 +144,7 @@ Bug Fixes: behavior. (Maurício Linhares) ### 3.0.0.beta1 / 2013-11-07 -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.rc1...v3.0.0.beta1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0...v3.0.0.beta1) Breaking Changes for 3.0.0: @@ -207,6 +207,11 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.0 / 2014-06-01 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.rc1...v2.99.0) + +No changes. Just taking it out of pre-release. + ### 2.99.0.rc1 / 2014-05-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta2...v2.99.0.rc1) From 0cd56799511ba759bec3c70a6de4a148b015f66e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 1 Jun 2014 20:23:51 -0700 Subject: [PATCH 0628/1179] Update changelog for v3.0.0. [ci skip] --- Changelog.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index d8653994b..06684c762 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.0.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.rc1...master) +### 3.0.0 / 2014-06-01 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.rc1...v3.0.0) Bug Fixes: @@ -18,9 +18,6 @@ Bug Fixes: considering stubs when checking the order. (Myron Marston) * Message expectation matchers now show descriptions from argument matchers when their expectations aren't met. (Jon Rowe) - -Enhancements: - * Display warning when encountering `TypeError` during instance method staging on 2.0.0-p195, suffers from https://bugs.ruby-lang.org/issues/8686 too. (Cezar Halmagean). From 58bc906e763d397cd65e87458eba783e02aa4911 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 1 Jun 2014 20:27:10 -0700 Subject: [PATCH 0629/1179] Release 3.0.0 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 181fc1bb7..eabdbcb71 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.0.0.rc1' + STRING = '3.0.0' end end end From 6efcb3b8b4beadc5b646d382f001197cc52c80ac Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 5 Jun 2014 09:35:35 +1000 Subject: [PATCH 0630/1179] Remove inch badge [skip ci] --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ce477c27c..36742cacd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) [![Inline docs](http://inch-pages.github.io/github/rspec/rspec-mocks.png)](http://inch-pages.github.io/github/rspec/rspec-mocks) - +# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) rspec-mocks is a test-double framework for rspec with support for method stubs, fakes, and message expectations on generated test-doubles and real objects alike. From 35caaaa9eeed86565713674254fa7eba42d3da04 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 5 Jun 2014 11:41:41 +1000 Subject: [PATCH 0631/1179] allow receive_message_chain to be constained by arguments --- lib/rspec/mocks/matchers/receive_message_chain.rb | 2 +- spec/rspec/mocks/matchers/receive_message_chain_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index 39365ee4c..d45e493e9 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -11,7 +11,7 @@ def initialize(chain, &block) @recorded_customizations = [] end - [:and_return, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg| + [:with, :and_return, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg| define_method(msg) do |*args, &block| @recorded_customizations << ExpectationCustomization.new(msg, args, block) self diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index 696a5d075..c5620149a 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -57,6 +57,13 @@ module RSpec::Mocks::Matchers expect(object.to_a.length).to eq(3) end + it "can constrain the return value by the argument to the last call" do + allow(object).to receive_message_chain(:one, :plus).with(1) { 2 } + allow(object).to receive_message_chain(:one, :plus).with(2) { 3 } + expect(object.one.plus(1)).to eq(2) + expect(object.one.plus(2)).to eq(3) + end + it "works with and_call_original", :pending => "See https://github.com/rspec/rspec-mocks/pull/467#issuecomment-28631621" do list = [1, 2, 3] expect(list).to receive_message_chain(:to_a, :length).and_call_original From 4f55dc6e38a390fd285cfa5159df15084a5759ce Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 5 Jun 2014 13:40:45 +1000 Subject: [PATCH 0632/1179] changelog for #697 --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index 06684c762..1339d1d7c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### 3.0.1 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...master) + +Enhancements: + +* Support constraining message chains by their arguments. (e.g. + `receive_message_chain(...).with(...)` ). (Jon Rowe, #697) + ### 3.0.0 / 2014-06-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.rc1...v3.0.0) From 5ba9e26249aff67fd4e1eb9f6a0c8d0a54e5f79e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 5 Jun 2014 13:51:56 +1000 Subject: [PATCH 0633/1179] correct to changelog for #697 --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 1339d1d7c..b16d06aef 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ ### 3.0.1 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...master) -Enhancements: +Bug Fixes: * Support constraining message chains by their arguments. (e.g. `receive_message_chain(...).with(...)` ). (Jon Rowe, #697) From 718bb92685710889eaa2f382eae41d752bf093ba Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 4 Jun 2014 23:33:39 -0700 Subject: [PATCH 0634/1179] Clarify some docs. --- README.md | 6 +++--- lib/rspec/mocks/example_methods.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ce477c27c..036dca59a 100644 --- a/README.md +++ b/README.md @@ -334,7 +334,7 @@ Instead of `before(:context)`, use `before(:example)`. rspec-mocks provides two methods, `allow_any_instance_of` and `expect_any_instance_of`, that will allow you to stub or mock any instance -of a class. They are used in place for `allow` or `expect`: +of a class. They are used in place of `allow` or `expect`: ```ruby allow_any_instance_of(Widget).to receive(:name).and_return("Wibble") @@ -351,8 +351,8 @@ general we discourage its use for a number of reasons: feature operates on entire classes of objects. As a result there are some sematically confusing edge cases. For example in `expect_any_instance_of(Widget).to receive(:name).twice` it isn't clear - whether each specific instance is allowed to receive `name` twice, or if two - receives total are allowed. (It's the former.) + whether each specific instance is expected to receive `name` twice, or if two + receives total are expected. (It's the former.) * Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too complex. diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 82d3579e4..1bf149227 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -256,7 +256,7 @@ def have_received(method_name, &block) # use of `receive_message_chain` a code smell. Even though not all code smells # indicate real problems (think fluent interfaces), `receive_message_chain` still # results in brittle examples. For example, if you write - # `foo.receive_message_chain(:bar, :baz => 37)` in a spec and then the + # `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the # implementation calls `foo.baz.bar`, the stub will not work. # # @example From 38712c6cf6ae1014d942d72e6eb3b673d71c1b90 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 27 May 2014 13:33:35 -0700 Subject: [PATCH 0635/1179] Add staging environment for relish. --- Rakefile | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Rakefile b/Rakefile index c59a318c5..492f66fb4 100644 --- a/Rakefile +++ b/Rakefile @@ -30,17 +30,34 @@ namespace :clobber do end end +with_changelog_in_features = lambda do |&block| + begin + sh "cp Changelog.md features/" + block.call + ensure + sh "rm features/Changelog.md" + end +end + desc "Push docs/cukes to relishapp using the relish-client-gem" task :relish, :version do |t, args| raise "rake relish[VERSION]" unless args[:version] - sh "cp Changelog.md features/" - if `relish versions rspec/rspec-mocks`.split.map(&:strip).include? args[:version] - puts "Version #{args[:version]} already exists" - else - sh "relish versions:add rspec/rspec-mocks:#{args[:version]}" + + with_changelog_in_features.call do + if `relish versions rspec/rspec-mocks`.split.map(&:strip).include? args[:version] + puts "Version #{args[:version]} already exists" + else + sh "relish versions:add rspec/rspec-mocks:#{args[:version]}" + end + sh "relish push rspec/rspec-mocks:#{args[:version]}" + end +end + +desc "Push to relish staging environment" +task :relish_staging do + with_changelog_in_features.call do + sh "relish push rspec-staging/rspec-mocks" end - sh "relish push rspec/rspec-mocks:#{args[:version]}" - sh "rm features/Changelog.md" end task :default => [:spec, :cucumber] From 0ecaa32ddd2434ae9abddc5a83d0be940d3d2702 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 4 Jun 2014 23:35:02 -0700 Subject: [PATCH 0636/1179] Convert a cuke scenario to specs. This has little documentation value but is useful as a regression test. --- features/argument_matchers/explicit.feature | 28 --------------------- spec/rspec/mocks/stub_spec.rb | 18 +++++++++++++ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/features/argument_matchers/explicit.feature b/features/argument_matchers/explicit.feature index 2f8ccbc08..99e6997d2 100644 --- a/features/argument_matchers/explicit.feature +++ b/features/argument_matchers/explicit.feature @@ -29,31 +29,3 @@ Feature: explicit arguments """ When I run `rspec stub_explicit_args_spec.rb` Then the output should contain "2 examples, 0 failures" - - Scenario: explicit arguments with multiple arities - Given a file named "stub_multiple_explicit_args_spec.rb" with: - """ruby - describe "stubbed multiple explicit arguments" do - it "works on stubs" do - object = Object.new - allow(object).to receive(:foo).with(:this) do |arg| - "got this" - end - allow(object).to receive(:foo).with(:this, :that) do |arg1, arg2| - "got this and that" - end - - expect(object.foo(:this)).to eq("got this") - expect(object.foo(:this, :that)).to eq("got this and that") - end - - it "works on mocks" do - object = double('foo') - expect(object).to receive(:foo).with(:this, :that) - - object.foo(:this, :that) - end - end - """ - When I run `rspec stub_multiple_explicit_args_spec.rb` - Then the output should contain "2 examples, 0 failures" diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 630072310..2c226d94c 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -84,6 +84,24 @@ def existing_private_instance_method expect{ @stub.dup.foobar }.to raise_error NoMethodError, /foobar/ end + context "using `with`" do + it 'determines which value is returned' do + allow(@stub).to receive(:foo).with(1) { :one } + allow(@stub).to receive(:foo).with(2) { :two } + + expect(@stub.foo(2)).to eq(:two) + expect(@stub.foo(1)).to eq(:one) + end + + it 'allows differing arities' do + allow(@stub).to receive(:foo).with(:two, :args) { :two_args } + allow(@stub).to receive(:foo).with(:three, :args, :total) { :three_args_total } + + expect(@stub.foo(:two, :args)).to eq(:two_args) + expect(@stub.foo(:three, :args, :total)).to eq(:three_args_total) + end + end + context "stubbing with prepend", :if => Support::RubyFeatures.module_prepends_supported? do module ToBePrepended def value From 8338a78be01e5638c0758b42c70377d571b0cdc7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 4 Jun 2014 23:32:43 -0700 Subject: [PATCH 0637/1179] Rewrite cucumber features. The old cukes were a mess: - Lots of duplication and inconsistencies. - Functioned poorly as documentation. - Didn't highlight the new syntax well. - Didn't really cover all of rspec-mocks' features. The new ones were structurd specifically with documentation in mind. The specs are meant more for regression coverage. We've limited the cukes to just things people may actually want to read as part of docs. I've also reworked the test unit cuke as a minitest cuke. For reference, here's the before/after from cucumber: Before: 77 scenarios (77 passed) 276 steps (276 passed) 0m48.014s After: 89 scenarios (89 passed) 328 steps (328 passed) 0m57.844s Fixes #591. --- Gemfile | 1 - features/.nav | 60 ++--- features/README.md | 93 ++++---- features/Scope.md | 17 -- features/argument_matchers/README.md | 27 --- features/argument_matchers/explicit.feature | 31 --- .../general_matchers.feature | 85 ------- .../argument_matchers/type_matchers.feature | 26 --- features/basics/allowing_messages.feature | 35 +++ features/basics/expecting_messages.feature | 73 ++++++ features/basics/null_object_doubles.feature | 35 +++ features/basics/partial_test_doubles.feature | 70 ++++++ features/basics/scope.feature | 99 +++++++++ features/basics/spies.feature | 144 ++++++++++++ features/basics/test_doubles.feature | 41 ++++ features/configuring_responses/README.md | 12 + .../block_implementation.feature | 131 +++++++++++ ...alling_the_original_implementation.feature | 52 +++++ .../raising_an_error.feature | 28 +++ .../returning_a_value.feature | 52 +++++ .../configuring_responses/throwing.feature | 36 +++ .../configuring_responses/yielding.feature | 76 +++++++ features/message_expectations/README.md | 75 ------- .../allow_any_instance_of.feature | 26 --- .../message_expectations/any_instance.feature | 43 ---- .../block_local_expectations.feature.pending | 55 ----- .../call_original.feature | 23 -- .../expect_message_using_expect.feature | 107 --------- ...xpect_message_using_should_receive.feature | 118 ---------- .../message_chains_using_expect.feature | 49 ---- .../receive_counts.feature | 209 ------------------ ...arn_when_expectation_is_set_on_nil.feature | 50 ----- features/method_stubs/README.md | 77 ------- .../allow_any_instance_of.feature | 136 ------------ features/method_stubs/as_null_object.feature | 40 ---- .../receive_message_chain.feature | 51 ----- .../simple_return_value_with_allow.feature | 44 ---- .../simple_return_value_with_stub.feature | 64 ------ .../method_stubs/stub_implementation.feature | 48 ---- features/method_stubs/to_ary.feature | 51 ----- features/mutating_constants/README.md | 22 +- ....feature => hide_defined_constant.feature} | 8 +- .../hide_undefined_constant.feature | 22 ++ .../stub_defined_constant.feature | 11 +- .../stub_undefined_constant.feature | 12 +- features/old_syntax/README.md | 35 +++ features/old_syntax/any_instance.feature | 105 +++++++++ features/old_syntax/should_receive.feature | 90 ++++++++ features/old_syntax/stub.feature | 51 +++++ features/old_syntax/stub_chain.feature | 69 ++++++ features/old_syntax/unstub.feature | 43 ++++ features/outside_rspec/minitest.feature | 80 +++++++ features/outside_rspec/standalone.feature | 20 +- features/setting_constraints/README.md | 6 + .../matching_arguments.feature | 97 ++++++++ .../setting_constraints/message_order.feature | 63 ++++++ .../receive_counts.feature | 189 ++++++++++++++++ .../spies/spy_partial_mock_method.feature | 34 --- features/spies/spy_pure_mock_method.feature | 76 ------- features/spies/spy_unstubbed_method.feature | 18 -- .../step_definitions/additional_cli_steps.rb | 20 +- features/support/disallow_certain_apis.rb | 24 ++ features/test_frameworks/test_unit.feature | 54 ----- features/verifying_doubles/README.md | 26 +-- .../verifying_doubles/class_doubles.feature | 19 +- .../verifying_doubles/dynamic_classes.feature | 18 +- .../instance_doubles.feature | 14 +- .../verifying_doubles/object_doubles.feature | 29 ++- .../verifying_doubles/partial_doubles.feature | 10 +- features/working_with_legacy_code/README.md | 3 + .../any_instance.feature | 115 ++++++++++ .../message_chains.feature | 79 +++++++ rspec-mocks.gemspec | 3 +- 73 files changed, 2139 insertions(+), 1816 deletions(-) delete mode 100644 features/Scope.md delete mode 100644 features/argument_matchers/README.md delete mode 100644 features/argument_matchers/explicit.feature delete mode 100644 features/argument_matchers/general_matchers.feature delete mode 100644 features/argument_matchers/type_matchers.feature create mode 100644 features/basics/allowing_messages.feature create mode 100644 features/basics/expecting_messages.feature create mode 100644 features/basics/null_object_doubles.feature create mode 100644 features/basics/partial_test_doubles.feature create mode 100644 features/basics/scope.feature create mode 100644 features/basics/spies.feature create mode 100644 features/basics/test_doubles.feature create mode 100644 features/configuring_responses/README.md create mode 100644 features/configuring_responses/block_implementation.feature create mode 100644 features/configuring_responses/calling_the_original_implementation.feature create mode 100644 features/configuring_responses/raising_an_error.feature create mode 100644 features/configuring_responses/returning_a_value.feature create mode 100644 features/configuring_responses/throwing.feature create mode 100644 features/configuring_responses/yielding.feature delete mode 100644 features/message_expectations/README.md delete mode 100644 features/message_expectations/allow_any_instance_of.feature delete mode 100644 features/message_expectations/any_instance.feature delete mode 100644 features/message_expectations/block_local_expectations.feature.pending delete mode 100644 features/message_expectations/call_original.feature delete mode 100644 features/message_expectations/expect_message_using_expect.feature delete mode 100644 features/message_expectations/expect_message_using_should_receive.feature delete mode 100644 features/message_expectations/message_chains_using_expect.feature delete mode 100644 features/message_expectations/receive_counts.feature delete mode 100644 features/message_expectations/warn_when_expectation_is_set_on_nil.feature delete mode 100644 features/method_stubs/README.md delete mode 100644 features/method_stubs/allow_any_instance_of.feature delete mode 100644 features/method_stubs/as_null_object.feature delete mode 100644 features/method_stubs/receive_message_chain.feature delete mode 100644 features/method_stubs/simple_return_value_with_allow.feature delete mode 100644 features/method_stubs/simple_return_value_with_stub.feature delete mode 100644 features/method_stubs/stub_implementation.feature delete mode 100644 features/method_stubs/to_ary.feature rename features/mutating_constants/{hiding_defined_constant.feature => hide_defined_constant.feature} (92%) create mode 100644 features/mutating_constants/hide_undefined_constant.feature create mode 100644 features/old_syntax/README.md create mode 100644 features/old_syntax/any_instance.feature create mode 100644 features/old_syntax/should_receive.feature create mode 100644 features/old_syntax/stub.feature create mode 100644 features/old_syntax/stub_chain.feature create mode 100644 features/old_syntax/unstub.feature create mode 100644 features/outside_rspec/minitest.feature create mode 100644 features/setting_constraints/README.md create mode 100644 features/setting_constraints/matching_arguments.feature create mode 100644 features/setting_constraints/message_order.feature create mode 100644 features/setting_constraints/receive_counts.feature delete mode 100644 features/spies/spy_partial_mock_method.feature delete mode 100644 features/spies/spy_pure_mock_method.feature delete mode 100644 features/spies/spy_unstubbed_method.feature create mode 100644 features/support/disallow_certain_apis.rb delete mode 100644 features/test_frameworks/test_unit.feature create mode 100644 features/working_with_legacy_code/README.md create mode 100644 features/working_with_legacy_code/any_instance.feature create mode 100644 features/working_with_legacy_code/message_chains.feature diff --git a/Gemfile b/Gemfile index b44027b3f..53e87a698 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,6 @@ end platforms :rbx do gem 'rubysl' - gem 'rubysl-test-unit' end eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') diff --git a/features/.nav b/features/.nav index 32f640c0a..63ad3e3a5 100644 --- a/features/.nav +++ b/features/.nav @@ -1,33 +1,43 @@ -- Upgrade.md -- Scope.md -- Changelog.md -- method_stubs: - - simple_return_value.feature - - stub_implementation.feature - - stub_chain.feature - - stub_with_arguments.feature - - any_instance.feature - - as_null_object.feature - - to_ary.feature -- message_expectations: - - expect_message.feature - - any_instance.feature - - block_local_expectations.feature.pending - - warn_when_expectation_is_set_on_nil.feature -- argument_matchers: - - explicit.feature - - general_matchers.feature - - type_matchers.feature -- mutating_constants: - - stub_defined_constant.feature - - stub_undefined_constant.feature - - hiding_defined_constant.feature +- basics: + - scope.feature + - test_doubles.feature + - allowing_messages.feature + - expecting_messages.feature + - partial_test_doubles.feature + - null_object_doubles.feature + - spies.feature - verifying_doubles: - instance_doubles.feature - class_doubles.feature - object_doubles.feature - dynamic_classes.feature - partial_doubles.feature +- configuring_responses: + - returning_a_value.feature + - raising_an_error.feature + - throwing.feature + - yielding.feature + - calling_the_original_implementation.feature + - block_implementation.feature +- setting_constraints: + - matching_arguments.feature + - receive_counts.feature + - message_order.feature +- mutating_constants: + - stub_defined_constant.feature + - stub_undefined_constant.feature + - hide_defined_constant.feature + - hide_undefined_constant.feature +- working_with_legacy_code: + - any_instance.feature + - message_chains.feature +- old_syntax: + - stub.feature + - should_receive.feature + - any_instance.feature + - stub_chain.feature + - unstub.feature - outside_rspec: - - configuration.feature + - minitest.feature - standalone.feature +- Changelog diff --git a/features/README.md b/features/README.md index 74fe2a955..0d988dfff 100644 --- a/features/README.md +++ b/features/README.md @@ -1,75 +1,76 @@ -rspec-mocks helps to control the context in a code example by letting you set -known return values, fake implementations of methods, and even expectations -that specific messages are received by an object. +rspec-mocks helps to control the context in a code example by letting you set known return +values, fake implementations of methods, and even set expectations that specific messages +are received by an object. -You can do these three things on test doubles that rspec-mocks creates for you -on the fly, or you can do them on objects that are part of your system. +You can do these three things on test doubles that rspec-mocks creates for you on the fly, or +you can do them on objects that are part of your system. ## Messages and Methods -_Message_ and _method_ are metaphors that we use somewhat interchangeably, but -they are subtly different. In Object Oriented Programming, objects communicate -by sending _messages_ to one another. When an object receives a message, it -invokes a _method_ with the same name as the message. +_Message_ and _method_ are metaphors that we use somewhat interchangeably, but they are +subtly different. In Object Oriented Programming, objects communicate by sending +_messages_ to one another. When an object receives a message, it invokes a _method_ with the +same name as the message. ## Test Doubles -A test double is an object that stands in for another object in your system -during a code example. Use the `double` method, passing in an optional identifier, to create one: +A test double is an object that stands in for another object in your system during a code +example. Use the `double` method, passing in an optional identifier, to create one: - book = double("book") +```ruby +book = double("book") +``` -Most of the time you will want some confidence that your doubles resemble an -existing object in your system. Verifying doubles are provided for this -purpose. If the existing object is available, they will prevent you from adding -stubs and expectations for methods that do not exist or that have an invalid -number of parameters. +Most of the time you will want some confidence that your doubles resemble an existing +object in your system. Verifying doubles are provided for this purpose. If the existing object +is available, they will prevent you from adding stubs and expectations for methods that do +not exist or that have invalid arguments. - book = instance_double("Book", :pages => 250) +```ruby +book = instance_double("Book", :pages => 250) +``` -Verifying doubles have some clever tricks to enable you to both test in -isolation without your dependencies loaded while still being able to validate -them against real objects. +[Verifying doubles](./docs/verifying-doubles) have some clever tricks to enable you to both test in isolation without your +dependencies loaded while still being able to validate them against real objects. ## Method Stubs A method stub is an instruction to an object (real or test double) to return a known value in response to a message: - allow(die).to receive(:roll) { 3 } +```ruby +allow(die).to receive(:roll) { 3 } +``` -This tells the `die` object to return the value `3` when it receives the `roll` -message. +This tells the `die` object to return the value `3` when it receives the `roll` message. ## Message Expectations -A message expectation is an expectation that an object should receive a -specific message during the course of a code example: +A message expectation is an expectation that an object should receive a specific message +during the course of a code example: - describe Account do - context "when closed" do - it "logs an 'account closed' message" do - logger = double() - account = Account.new - account.logger = logger +```ruby +describe Account do + context "when closed" do + it "logs an 'account closed' message" do + logger = double() + account = Account.new + account.logger = logger - expect(logger).to receive(:account_closed).with(account) + expect(logger).to receive(:account_closed).with(account) - account.close - end - end + account.close end + end +end +``` -This example specifies that the `account` object sends the `logger` the -`account_closed` message (with itself as an argument) when it receives the -`close` message. +This example specifies that the `account` object sends the `logger` the `account_closed` +message (with itself as an argument) when it receives the `close` message. ## Issues -The documentation for rspec-mocks is a work in progress. We'll be adding -Cucumber features over time, and clarifying existing ones. If you have -specific features you'd like to see added, find the existing documentation -incomplete or confusing, or, better yet, wish to write a missing Cucumber -feature yourself, please [submit an -issue](http://github.com/rspec/rspec-mocks/issues) or a [pull -request](http://github.com/rspec/rspec-mocks). +The documentation for rspec-mocks is a work in progress. We'll be adding Cucumber +features over time, and clarifying existing ones. If you have specific features you'd like to see +added, find the existing documentation incomplete or confusing, or, better yet, wish to write +a missing Cucumber feature yourself, please [submit an issue](http://github.com/rspec/rspec-mocks/issues) or a [pull request](http://github.com/rspec/rspec-mocks). diff --git a/features/Scope.md b/features/Scope.md deleted file mode 100644 index 8d11d29ef..000000000 --- a/features/Scope.md +++ /dev/null @@ -1,17 +0,0 @@ -Doubles, stubs, and message expectations are all cleaned out after each -example. This ensures that each example can be run in isolation, and in any -order. - -### `before(:example)` - -It is perfectly fine to set up doubles, stubs, and message expectations in -a `before(:example)` hook, as that hook is executed in the scope of the example: - - before(:example) do - @account = double('account') - end - -### Do not create doubles, stubs, or message expectations in `before(:context)` - -If you do, they'll get cleaned out after the first example, and you will be -very confused as to what's going on in the second example. diff --git a/features/argument_matchers/README.md b/features/argument_matchers/README.md deleted file mode 100644 index 423ba997f..000000000 --- a/features/argument_matchers/README.md +++ /dev/null @@ -1,27 +0,0 @@ -### Introduction - -Argument matchers can be used: - -* In stubs to constrain the scope of the stubbed method - - allow(obj).to receive(:foo).with(:bar) do |arg| - #do something for :bar - end - allow(obj).to receive(:foo).with(:baz) do |arg| - #do something for :baz - end - -* In expectations to validate the arguments that should be received in a method call - - #create a double - obj = double() - - #expect a message with given args - expect(obj).to receive(:message).with('an argument') - -If more control is needed, one can use a block - - expect(obj).to receive(:message) do |arg1, arg2| - # set expectations about the args in this block - # and optionally set a return value - end diff --git a/features/argument_matchers/explicit.feature b/features/argument_matchers/explicit.feature deleted file mode 100644 index 99e6997d2..000000000 --- a/features/argument_matchers/explicit.feature +++ /dev/null @@ -1,31 +0,0 @@ -Feature: explicit arguments - - Allows you to explicitly specify the argument values - - Scenario: explicit arguments - Given a file named "stub_explicit_args_spec.rb" with: - """ruby - describe "stubbed explicit arguments" do - it "works on stubs" do - object = Object.new - allow(object).to receive(:foo).with(:this) do |arg| - "got this" - end - allow(object).to receive(:foo).with(:that) do |arg| - "got that" - end - - expect(object.foo(:this)).to eq("got this") - expect(object.foo(:that)).to eq("got that") - end - - it "works on doubles and expectations" do - object = double('foo') - expect(object).to receive(:bar).with(:foo) - - object.bar(:foo) - end - end - """ - When I run `rspec stub_explicit_args_spec.rb` - Then the output should contain "2 examples, 0 failures" diff --git a/features/argument_matchers/general_matchers.feature b/features/argument_matchers/general_matchers.feature deleted file mode 100644 index 117f7fa5b..000000000 --- a/features/argument_matchers/general_matchers.feature +++ /dev/null @@ -1,85 +0,0 @@ -Feature: General matchers - - The `anything`, `any_args`, and `no_args` matchers can be used to require the method - to have arguments (or not) without constraining the details of the argument, such as its - type, pattern or value. The `anything` matcher only reflects a single argument, while - the `any_args` matcher matches any arity. - - Scenario: anything argument matcher - Given a file named "stub_anything_args_spec.rb" with: - """ruby - describe "stubbed anything() args spec" do - it "works" do - object = Object.new - allow(object).to receive(:foo).with(anything) do - "anything" - end - - expect(object.foo(1)).to eq("anything") - expect(object.foo(:that)).to eq("anything") - end - end - """ - When I run `rspec stub_anything_args_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: any_args argument matcher - Given a file named "stub_any_args_spec.rb" with: - """ruby - describe "stubbed any_args() args spec" do - it "works" do - object = Object.new - allow(object).to receive(:foo).with(any_args) do - "anything" - end - - expect(object.foo(1)).to eq("anything") - expect(object.foo(:that)).to eq("anything") - expect(object.foo).to eq("anything") - end - end - """ - When I run `rspec stub_any_args_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: no_args argument matcher - Given a file named "stub_no_args_spec.rb" with: - """ruby - describe "stubbed no_args() args spec" do - it "works for no args" do - object = Object.new - allow(object).to receive(:foo).with(no_args) do - "nothing" - end - allow(object).to receive(:foo).with(anything) do - "something" - end - - expect(object.foo(:that)).to eq("something") - expect(object.foo).to eq("nothing") - end - end - """ - When I run `rspec stub_no_args_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: no_args argument matcher for expectations - Given a file named "stub_no_args_expectations_spec.rb" with: - """ruby - describe "stubbed no_args() args spec for expectations" do - it "works for no args" do - object = Object.new - expect(object).to receive(:foo).with(no_args) - - object.foo - end - it "fails for args" do - object = Object.new - expect(object).to receive(:foo).with(no_args) - - object.foo(:bar) - end - end - """ - When I run `rspec stub_no_args_expectations_spec.rb` - Then the output should contain "2 examples, 1 failure" diff --git a/features/argument_matchers/type_matchers.feature b/features/argument_matchers/type_matchers.feature deleted file mode 100644 index 7b8183a93..000000000 --- a/features/argument_matchers/type_matchers.feature +++ /dev/null @@ -1,26 +0,0 @@ -Feature: stub with argument constraints - - You can further specify the behavior by constraining the type, - format and/or number of arguments with the `#with()` method - chained off of `#stub()` - - Scenario: an_instance_of argument matcher - Given a file named "stub_an_instance_of_args_spec.rb" with: - """ruby - describe "stubbed an_instance_of() args spec" do - it "works" do - object = Object.new - allow(object).to receive(:foo).with(an_instance_of(Symbol)) do - "symbol" - end - allow(object).to receive(:foo).with(an_instance_of(String)) do - "string" - end - - expect(object.foo("bar")).to eq("string") - expect(object.foo(:that)).to eq("symbol") - end - end - """ - When I run `rspec stub_an_instance_of_args_spec.rb` - Then the output should contain "1 example, 0 failures" diff --git a/features/basics/allowing_messages.feature b/features/basics/allowing_messages.feature new file mode 100644 index 000000000..d801f2544 --- /dev/null +++ b/features/basics/allowing_messages.feature @@ -0,0 +1,35 @@ +Feature: Allowing messages + + [Test doubles](./test-doubles) are "strict" by default -- messages that have not been specifically + allowed or expected will trigger an error. Use `allow(...).to receive(...)` to configure + which messages are the double is allowed to receive. You can also use `allow(...).to + receive_messages(...)` to configure allowed messages (and return values) in bulk. + + Scenario: Allowed messages return nil by default + Given a file named "allow_message_spec.rb" with: + """ruby + RSpec.describe "allow" do + it "returns nil from allowed messages" do + dbl = double("Some Collaborator") + allow(dbl).to receive(:foo) + expect(dbl.foo).to be_nil + end + end + """ + When I run `rspec allow_message_spec.rb` + Then the examples should all pass + + Scenario: Messages can be allowed in bulk using `receive_messages` + Given a file named "receive_messages_spec.rb" with: + """ruby + RSpec.describe "receive_messages" do + it "configures return values for the provided messages" do + dbl = double("Some Collaborator") + allow(dbl).to receive_messages(:foo => 2, :bar => 3) + expect(dbl.foo).to eq(2) + expect(dbl.bar).to eq(3) + end + end + """ + When I run `rspec receive_messages_spec.rb` + Then the examples should all pass diff --git a/features/basics/expecting_messages.feature b/features/basics/expecting_messages.feature new file mode 100644 index 000000000..f0ff91c02 --- /dev/null +++ b/features/basics/expecting_messages.feature @@ -0,0 +1,73 @@ +Feature: Expecting messages + + Use `expect(...).to receive(...)` to expect a message on a [test double](./test-doubles). Unfulfilled + message expectations trigger failures when the example completes. You can also use + `expect(...).not_to receive(...)` to set a negative message expectation. + + Scenario: Failing positive message expectation + Given a file named "unfulfilled_message_expectation_spec.rb" with: + """ruby + RSpec.describe "An unfulfilled positive message expectation" do + it "triggers a failure" do + dbl = double("Some Collaborator") + expect(dbl).to receive(:foo) + end + end + """ + When I run `rspec unfulfilled_message_expectation_spec.rb` + Then it should fail with: + """ + 1) An unfulfilled positive message expectation triggers a failure + Failure/Error: expect(dbl).to receive(:foo) + (Double "Some Collaborator").foo(any args) + expected: 1 time with any arguments + received: 0 times with any arguments + """ + + Scenario: Passing positive message expectation + Given a file named "fulfilled_message_expectation_spec.rb" with: + """ruby + RSpec.describe "A fulfilled positive message expectation" do + it "passes" do + dbl = double("Some Collaborator") + expect(dbl).to receive(:foo) + dbl.foo + end + end + """ + When I run `rspec fulfilled_message_expectation_spec.rb` + Then the examples should all pass + + Scenario: Failing negative message expectation + Given a file named "negative_message_expectation_spec.rb" with: + """ruby + RSpec.describe "A negative message expectation" do + it "fails when the message is received" do + dbl = double("Some Collaborator").as_null_object + expect(dbl).not_to receive(:foo) + dbl.foo + end + end + """ + When I run `rspec negative_message_expectation_spec.rb` + Then it should fail with: + """ + 1) A negative message expectation fails when the message is received + Failure/Error: dbl.foo + (Double "Some Collaborator").foo(no args) + expected: 0 times with any arguments + received: 1 time + """ + + Scenario: Passing negative message expectation + Given a file named "negative_message_expectation_spec.rb" with: + """ruby + RSpec.describe "A negative message expectation" do + it "passes if the message is never received" do + dbl = double("Some Collaborator").as_null_object + expect(dbl).not_to receive(:foo) + end + end + """ + When I run `rspec negative_message_expectation_spec.rb` + Then the examples should all pass diff --git a/features/basics/null_object_doubles.feature b/features/basics/null_object_doubles.feature new file mode 100644 index 000000000..b2b99533f --- /dev/null +++ b/features/basics/null_object_doubles.feature @@ -0,0 +1,35 @@ +Feature: Null object doubles + + [Test doubles](./test-doubles) are strict by default, raising errors when they receive messages that have not + been allowed or expected. You can chain `as_null_object` off of `double` in order to make + the double "loose". For any message that has not explicitly allowed or expected, the double + will return itself. It acts as a block-hole null object, allowing arbitrarily deep method chains. + + Scenario: `as_null_object` allows arbitrarily deep message chains and returns itself + Given a file named "as_null_object_spec.rb" with: + """ruby + RSpec.describe "as_null_object" do + it "returns itself" do + dbl = double("Some Collaborator").as_null_object + expect(dbl.foo.bar.bazz).to be(dbl) + end + end + """ + When I run `rspec as_null_object_spec.rb` + Then the examples should all pass + + Scenario: Individual methods can still be allowed or expected + Given a file named "as_null_object_spec.rb" with: + """ruby + RSpec.describe "as_null_object" do + it "can allow individual methods" do + dbl = double("Some Collaborator", :foo => 3).as_null_object + allow(dbl).to receive(:bar).and_return(4) + + expect(dbl.foo).to eq(3) + expect(dbl.bar).to eq(4) + end + end + """ + When I run `rspec as_null_object_spec.rb` + Then the examples should all pass diff --git a/features/basics/partial_test_doubles.feature b/features/basics/partial_test_doubles.feature new file mode 100644 index 000000000..2b6261f3e --- /dev/null +++ b/features/basics/partial_test_doubles.feature @@ -0,0 +1,70 @@ +Feature: Partial test doubles + + A _partial test double_ is an extension of a real object in a system that is instrumented with + test-double like behaviour in the context of a test. This technique is very common in Ruby + because we often see class objects acting as global namespaces for methods. For example, + in Rails: + + ```ruby + person = double("person") + allow(Person).to receive(:find) { person } + ``` + + In this case we're instrumenting Person to return the person object we've defined whenever + it receives the `find` message. We can also set a message expectation so that the example + fails if `find` is not called: + + ```ruby + person = double("person") + expect(Person).to receive(:find) { person } + ``` + + RSpec replaces the method we're stubbing or mocking with its own test-double-like method. + At the end of the example, RSpec verifies any message expectations, and then restores the + original methods. + + Note: we recommend enabling the [`verify_partial_doubles`](../verifying-doubles/partial-doubles) config option. + + Scenario: Only the specified methods are redefined + Given a file named "partial_double_spec.rb" with: + """ruby + RSpec.describe "A partial double" do + # Note: stubbing a string like this is a terrible idea. + # This is just for demonstration purposes. + let(:string) { "a string" } + before { allow(string).to receive(:length).and_return(500) } + + it "redefines the specified methods" do + expect(string.length).to eq(500) + end + + it "does not effect other methods" do + expect(string.reverse).to eq("gnirts a") + end + end + """ + When I run `rspec partial_double_spec.rb` + Then the examples should all pass + + Scenario: The original method is restored when the example completes + Given a file named "partial_double_spec.rb" with: + """ruby + class User + def self.find(id) + :original_return_value + end + end + + RSpec.describe "A partial double" do + it "redefines a method" do + allow(User).to receive(:find).and_return(:redefined) + expect(User.find(3)).to eq(:redefined) + end + + it "restores the redefined method after the example completes" do + expect(User.find(3)).to eq(:original_return_value) + end + end + """ + When I run `rspec partial_double_spec.rb --order defined` + Then the examples should all pass diff --git a/features/basics/scope.feature b/features/basics/scope.feature new file mode 100644 index 000000000..1bc8a430c --- /dev/null +++ b/features/basics/scope.feature @@ -0,0 +1,99 @@ +Feature: Scope + + All rspec-mocks constructs have a per-example lifecycle. Message expectations are verified + after each example. Doubles, method stubs, stubbed constants, etc. are all cleaned up after + each example. This ensures that each example can be run in isolation, and in any order. + + It is perfectly fine to set up doubles, stubs, and message expectations in a + `before(:example)` hook, as that hook is executed in the scope of the example: + + ```ruby + before(:example) do + allow(MyClass).to receive(:foo) + end + ``` + + Since `before(:context)` runs outside the scope of any individual example, usage of + rspec-mocks features is not supported there. You can, however, create a temporary scope in + _any_ arbitrary context, including in a `before(:context)` hook, using + `RSpec::Mocks.with_temporary_scope { }`. + + Scenario: Cannot create doubles in a `before(:context)` hook + Given a file named "before_context_spec.rb" with: + """ruby + RSpec.describe "Creating a double in a before(:context) hook" do + before(:context) do + @dbl = double(:foo => 13) + end + + it "fails before it gets to the examples" do + expect(@dbl.foo).to eq(13) + end + end + """ + When I run `rspec before_context_spec.rb` + Then it should fail with: + """ + The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported. + """ + + Scenario: Use `with_temporary_scope` to create and use a double in a `before(:context)` hook + Given a file named "with_temporary_scope_spec.rb" with: + """ruby + RSpec.describe "Creating a double in a before(:context) hook" do + before(:context) do + RSpec::Mocks.with_temporary_scope do + dbl = double(:foo => 13) + @result = dbl.foo + end + end + + it "allows a double to be created and used from within a with_temporary_scope block" do + expect(@result).to eq(13) + end + end + """ + When I run `rspec with_temporary_scope_spec.rb` + Then the examples should all pass + + Scenario: Doubles cannot be reused in another example + Given a file named "leak_test_double_spec.rb" with: + """ruby + class Account + class << self + attr_accessor :logger + end + + def initialize + @balance = 0 + end + + attr_reader :balance + + def credit(amount) + @balance += amount + self.class.logger.log("Credited $#{amount}") + end + end + + RSpec.describe Account do + it "logs each credit" do + Account.logger = logger = double("Logger") + expect(logger).to receive(:log).with("Credited $15") + account = Account.new + account.credit(15) + end + + it "keeps track of the balance" do + account = Account.new + expect { account.credit(10) }.to change { account.balance }.by(10) + end + end + """ + When I run `rspec leak_test_double_spec.rb` + Then it should fail with the following output: + | 2 examples, 1 failure | + | | + | 1) Account keeps track of the balance | + | Failure/Error: self.class.logger.log("Credited $#{amount}") | + | Double "Logger" was originally created in one example but has leaked into another example and can no longer be used. | diff --git a/features/basics/spies.feature b/features/basics/spies.feature new file mode 100644 index 000000000..78e2ffd8f --- /dev/null +++ b/features/basics/spies.feature @@ -0,0 +1,144 @@ +Feature: Spies + + [Message expectations](./expecting-messages) put an example's expectation at the start, before you've invoked the + code-under-test. Many developers prefer using an act-arrange-assert (or given-when-then) + pattern for structuring tests. Spies are an alternate type of test double that support this + pattern by allowing you to expect that a message has been received after the fact, using + `have_received`. + + You can use any test double (or partial double) as a spy, but the double must be setup to + spy on the messages you care about. [Null object doubles](./null-object-doubles) automatically spy on all messages, + or you can [allow a message](./allowing-messages) to spy on it. + + `have_received` supports the same fluent interface for [setting constraints](../setting-constraints) that normal message expectations do. + + Note: The `have_received` API shown here will only work if you are using rspec-expectations. + + Scenario: Use a null object double as a spy + Given a file named "null_object_spy_spec.rb" with: + """ruby + RSpec.describe "have_received" do + it "passes when the message has been received" do + invitation = double('invitation').as_null_object + invitation.deliver + expect(invitation).to have_received(:deliver) + end + end + """ + When I run `rspec null_object_spy_spec.rb` + Then the examples should all pass + + Scenario: Spy on a method on a partial double + Given a file named "partial_double_spy_spec.rb" with: + """ruby + class Invitation + def self.deliver; end + end + + RSpec.describe "have_received" do + it "passes when the expectation is met" do + allow(Invitation).to receive(:deliver) + Invitation.deliver + expect(Invitation).to have_received(:deliver) + end + end + """ + When I run `rspec partial_double_spy_spec.rb` + Then the examples should all pass + + Scenario: Failure when the message has not been received + Given a file named "failure_spec.rb" with: + """ruby + class Invitation + def self.deliver; end + end + + RSpec.describe "failure when the message has not been received" do + example "for a null object double" do + invitation = double('invitation').as_null_object + expect(invitation).to have_received(:deliver) + end + + example "for a partial double" do + allow(Invitation).to receive(:deliver) + expect(Invitation).to have_received(:deliver) + end + end + """ + When I run `rspec failure_spec.rb --order defined` + Then it should fail with: + """ + 1) failure when the message has not been received for a null object double + Failure/Error: expect(invitation).to have_received(:deliver) + (Double "invitation").deliver(any args) + expected: 1 time with any arguments + received: 0 times with any arguments + """ + And it should fail with: + """ + 2) failure when the message has not been received for a partial double + Failure/Error: expect(Invitation).to have_received(:deliver) + ().deliver(any args) + expected: 1 time with any arguments + received: 0 times with any arguments + """ + + Scenario: Set constraints using the fluent interface + Given a file named "setting_constraints_spec.rb" with: + """ruby + RSpec.describe "An invitiation" do + let(:invitation) { double("invitation").as_null_object } + + before do + invitation.deliver("foo@example.com") + invitation.deliver("bar@example.com") + end + + it "passes when a count constraint is satisfied" do + expect(invitation).to have_received(:deliver).twice + end + + it "passes when an order constraint is satisifed" do + expect(invitation).to have_received(:deliver).with("foo@example.com").ordered + expect(invitation).to have_received(:deliver).with("bar@example.com").ordered + end + + it "fails when a count constraint is not satisfied" do + expect(invitation).to have_received(:deliver).at_least(3).times + end + + it "fails when an order constraint is not satisifed" do + expect(invitation).to have_received(:deliver).with("bar@example.com").ordered + expect(invitation).to have_received(:deliver).with("foo@example.com").ordered + end + end + """ + When I run `rspec setting_constraints_spec.rb --order defined` + Then it should fail with the following output: + | 4 examples, 2 failures | + | | + | 1) An invitiation fails when a count constraint is not satisfied | + | Failure/Error: expect(invitation).to have_received(:deliver).at_least(3).times | + | (Double "invitation").deliver(any args) | + | expected: at least 3 times with any arguments | + | received: 2 times with any arguments | + | | + | 2) An invitiation fails when an order constraint is not satisifed | + | Failure/Error: expect(invitation).to have_received(:deliver).with("foo@example.com").ordered | + | Double "invitation" received :deliver out of order | + + Scenario: `have_received` generates a good example description + Given a file named "generates_description_spec.rb" with: + """ruby + RSpec.describe "An invitation" do + subject(:invitation) { double('invitation').as_null_object } + before { invitation.deliver } + it { is_expected.to have_received(:deliver) } + end + """ + When I run `rspec --format documentation generates_description_spec.rb` + Then it should pass with: + """ + An invitation + should have received deliver(any args) 1 time + """ diff --git a/features/basics/test_doubles.feature b/features/basics/test_doubles.feature new file mode 100644 index 000000000..80a1330e7 --- /dev/null +++ b/features/basics/test_doubles.feature @@ -0,0 +1,41 @@ +Feature: Test Doubles + + _Test double_ is a generic term for any object that stands in for a real object during a test + (think "stunt double"). You create one using the `double` method. Doubles are "strict" by + default -- any message you have not allowed or expected will trigger an error -- but you can + [switch a double to being "loose"](./null-object-doubles). When creating a double, you can allow messages (and set + their return values) by passing a hash. + + Once you have a test double, you can [allow](./allowing-messages) or [expect](./expecting-messages) messages on it. + + We recommend you use [verifying doubles](../verifying-doubles) whenever possible. + + Scenario: Doubles are strict by default + Given a file named "double_spec.rb" with: + """ruby + RSpec.describe "double" do + it "raises errors when messages not allowed or expected are received" do + dbl = double("Some Collaborator") + dbl.foo + end + end + """ + When I run `rspec double_spec.rb` + Then it should fail with: + """ + Double "Some Collaborator" received unexpected message :foo with (no args) + """ + + Scenario: A hash can be used to define allowed messages and return values + Given a file named "double_spec.rb" with: + """ruby + RSpec.describe "double" do + it "raises errors when messages not allowed or expected are received" do + dbl = double("Some Collaborator", :foo => 3, :bar => 4) + expect(dbl.foo).to eq(3) + expect(dbl.bar).to eq(4) + end + end + """ + When I run `rspec double_spec.rb` + Then the examples should all pass diff --git a/features/configuring_responses/README.md b/features/configuring_responses/README.md new file mode 100644 index 000000000..d593e7cab --- /dev/null +++ b/features/configuring_responses/README.md @@ -0,0 +1,12 @@ +When [allowing](../basics/allowing-messages) or [expecting](../basics/expecting-messages) messages, the default response is to return `nil`. Several +methods are provided to configure how the test double responds to the message. + +* `and_return` +* `and_raise` +* `and_throw` +* `and_yield` +* `and_call_original` + +In addition, you can provide a [block implementation](./block-implementation) to respond in any manner you wish. + +Note: for simplicity, the examples here use `allow` rather than `expect`, but these APIs apply equally to both cases. diff --git a/features/configuring_responses/block_implementation.feature b/features/configuring_responses/block_implementation.feature new file mode 100644 index 000000000..8f1449a11 --- /dev/null +++ b/features/configuring_responses/block_implementation.feature @@ -0,0 +1,131 @@ +Feature: Block implementation + + When you pass a block, RSpec will use your block as the implementation of the method. Any + arguments (or a block) provided by the caller will be yielded to your block implementation. + This feature is extremely flexible, and supports many use cases that are not directly + supported by the more declaritive fluent interface. + + You can pass a block to any of the fluent interface methods: + + * `allow(dbl).to receive(:foo) { do_something }` + * `allow(dbl).to receive(:foo).with("args") { do_something }` + * `allow(dbl).to receive(:foo).once { do_something }` + * `allow(dbl).to receive(:foo).ordered { do_something }` + + Some of the more common use cases for block implementations are shown below, but this + is not an exhaustive list. + + Scenario: Use a block to specify a return value with a terser syntax + Given a file named "return_value_spec.rb" with: + """ruby + RSpec.describe "Specifying a return value using a block" do + it "returns the block's return value" do + dbl = double + allow(dbl).to receive(:foo) { 14 } + expect(dbl.foo).to eq(14) + end + end + """ + When I run `rspec return_value_spec.rb` + Then the examples should all pass + + Scenario: Use a block to verify arguments + Given a file named "verify_arguments_spec.rb" with: + """ruby + RSpec.describe "Verifying arguments using a block" do + it "fails when the arguments do not meet the expectations set in the block" do + dbl = double + + allow(dbl).to receive(:foo) do |arg| + expect(arg).to eq("bar") + end + + dbl.foo(nil) + end + end + """ + When I run `rspec verify_arguments_spec.rb` + Then it should fail with: + """ + Failure/Error: expect(arg).to eq("bar") + """ + + Scenario: Use a block to perform a calculation + Given a file named "perform_calculation_spec.rb" with: + """ruby + RSpec.describe "Performing a calculation using a block" do + it "returns the block's return value" do + loan = double("Loan", :amount => 100) + + allow(loan).to receive(:required_payment_for_rate) do |rate| + loan.amount * rate + end + + expect(loan.required_payment_for_rate(0.05)).to eq(5) + expect(loan.required_payment_for_rate(0.1)).to eq(10) + end + end + """ + When I run `rspec perform_calculation_spec.rb` + Then the examples should all pass + + Scenario: Yield to the caller's block + Given a file named "yield_to_caller_spec.rb" with: + """ruby + RSpec.describe "When the caller passes a block" do + it "can be yielded to from your implementation block" do + dbl = double + allow(dbl).to receive(:foo) { |&block| block.call(14) } + expect { |probe| dbl.foo(&probe) }.to yield_with_args(14) + end + end + """ + When I run `rspec yield_to_caller_spec.rb` + Then the examples should all pass + + Scenario: Delegate to partial double's original implementation within the block + Given a file named "delegate_to_original_spec.rb" with: + """ruby + class Calculator + def self.add(x, y) + x + y + end + end + + RSpec.describe "When using a block implementation on a partial double" do + it "supports delegating to the original implementation" do + original_add = Calculator.method(:add) + + allow(Calculator).to receive(:add) do |x, y| + original_add.call(x, y) * 2 + end + + expect(Calculator.add(2, 5)).to eq(14) + end + end + """ + When I run `rspec delegate_to_original_spec.rb` + Then the examples should all pass + + Scenario: Simulating a transient network failure + Given a file named "simulate_transient_network_failure_spec.rb" with: + """ruby + RSpec.describe "An HTTP API client" do + it "can simulate transient network failures" do + client = double("MyHTTPClient") + + call_count = 0 + allow(client).to receive(:fetch_data) do + call_count += 1 + call_count.odd? ? raise("timeout") : { :count => 15 } + end + + expect { client.fetch_data }.to raise_error("timeout") + expect(client.fetch_data).to eq(:count => 15) + expect { client.fetch_data }.to raise_error("timeout") + expect(client.fetch_data).to eq(:count => 15) + end + end + """ + When I run `rspec simulate_transient_network_failure_spec.rb` + Then the examples should all pass diff --git a/features/configuring_responses/calling_the_original_implementation.feature b/features/configuring_responses/calling_the_original_implementation.feature new file mode 100644 index 000000000..ecadf355c --- /dev/null +++ b/features/configuring_responses/calling_the_original_implementation.feature @@ -0,0 +1,52 @@ +Feature: Calling the original implementation + + Use `and_call_original` to make a partial double response as it normally would. This can + be useful when you want to expect a message without interfering with how it responds. You + can also use it to configure the default response for most arguments, and then override + that for specific arguments using `with`. + + Note: `and_call_original` is only supported on partial doubles, as normal test doubles do + not have an original implementation. + + Background: + Given a file named "lib/calculator.rb" with: + """ruby + class Calculator + def self.add(x, y) + x + y + end + end + """ + + Scenario: `and_call_original` makes the partial double respond as it normally would + Given a file named "spec/and_call_original_spec.rb" with: + """ruby + require 'calculator' + + RSpec.describe "and_call_original" do + it "responds as it normally would" do + expect(Calculator).to receive(:add).and_call_original + expect(Calculator.add(2, 3)).to eq(5) + end + end + """ + When I run `rspec spec/and_call_original_spec.rb` + Then the examples should all pass + + Scenario: `and_call_original` can configure a default response that can be overriden for specific args + Given a file named "spec/and_call_original_spec.rb" with: + """ruby + require 'calculator' + + RSpec.describe "and_call_original" do + it "can be overriden for specific arguments using #with" do + allow(Calculator).to receive(:add).and_call_original + allow(Calculator).to receive(:add).with(2, 3).and_return(-5) + + expect(Calculator.add(2, 2)).to eq(4) + expect(Calculator.add(2, 3)).to eq(-5) + end + end + """ + When I run `rspec spec/and_call_original_spec.rb` + Then the examples should all pass diff --git a/features/configuring_responses/raising_an_error.feature b/features/configuring_responses/raising_an_error.feature new file mode 100644 index 000000000..c8d212ada --- /dev/null +++ b/features/configuring_responses/raising_an_error.feature @@ -0,0 +1,28 @@ +Feature: Raising an error + + Use `and_raise` to make the test double raise an error when it receives the message. Any of the following forms are supported: + + * `and_raise(ExceptionClass)` + * `and_raise("message")` + * `and_raise(ExceptionClass, "message")` + * `and_raise(instance_of_an_exception_class)` + + Scenario: Raising an error + Given a file named "raises_an_error_spec.rb" with: + """ruby + RSpec.describe "Making it raise an error" do + it "raises the provided exception" do + dbl = double + allow(dbl).to receive(:foo).and_raise("boom") + dbl.foo + end + end + """ + When I run `rspec raises_an_error_spec.rb` + Then it should fail with: + """ + 1) Making it raise an error raises the provided exception + Failure/Error: dbl.foo + RuntimeError: + boom + """ diff --git a/features/configuring_responses/returning_a_value.feature b/features/configuring_responses/returning_a_value.feature new file mode 100644 index 000000000..623b251d3 --- /dev/null +++ b/features/configuring_responses/returning_a_value.feature @@ -0,0 +1,52 @@ +Feature: Returning a value + + Use `and_return` to specify a return value. Pass `and_return` multiple values to specify + different return values for consecutive calls. The final value will continue to be returned if + the message is received additional times. + + Scenario: Nil is returned by default + Given a file named "returns_nil_spec.rb" with: + """ruby + RSpec.describe "The default response" do + it "returns nil when no response has been configured" do + dbl = double + allow(dbl).to receive(:foo) + expect(dbl.foo).to be_nil + end + end + """ + When I run `rspec returns_nil_spec.rb` + Then the examples should all pass + + Scenario: Specify a return value + Given a file named "and_return_spec.rb" with: + """ruby + RSpec.describe "Specifying a return value" do + it "returns the specified return value" do + dbl = double + allow(dbl).to receive(:foo).and_return(14) + expect(dbl.foo).to eq(14) + end + end + """ + When I run `rspec and_return_spec.rb` + Then the examples should all pass + + Scenario: Specify different return values for multiple calls + Given a file named "multiple_calls_spec.rb" with: + """ruby + RSpec.describe "When the method is called multiple times" do + it "returns the specified values in order, then keeps returning the last value" do + dbl = double + allow(dbl).to receive(:foo).and_return(1, 2, 3) + + expect(dbl.foo).to eq(1) + expect(dbl.foo).to eq(2) + expect(dbl.foo).to eq(3) + expect(dbl.foo).to eq(3) + expect(dbl.foo).to eq(3) + end + end + """ + When I run `rspec multiple_calls_spec.rb` + Then the examples should all pass diff --git a/features/configuring_responses/throwing.feature b/features/configuring_responses/throwing.feature new file mode 100644 index 000000000..c6adbf9e5 --- /dev/null +++ b/features/configuring_responses/throwing.feature @@ -0,0 +1,36 @@ +Feature: Throwing + + Use `and_throw` to make the test double throw the provided symbol, optionally with the provided argument. + + * `and_throw(:symbol)` + * `and_throw(:symbol, argument)` + + Scenario: Throw a symbol + Given a file named "and_throw_spec.rb" with: + """ruby + RSpec.describe "Making it throw a symbol" do + it "throws the provided symbol" do + dbl = double + allow(dbl).to receive(:foo).and_throw(:hello) + + catch :hello do + dbl.foo + fail "should not get here" + end + end + + it "includes the provided argument when throwing" do + dbl = double + allow(dbl).to receive(:foo).and_throw(:hello, "world") + + arg = catch :hello do + dbl.foo + fail "should not get here" + end + + expect(arg).to eq("world") + end + end + """ + When I run `rspec and_throw_spec.rb` + Then the examples should all pass diff --git a/features/configuring_responses/yielding.feature b/features/configuring_responses/yielding.feature new file mode 100644 index 000000000..303e5ef49 --- /dev/null +++ b/features/configuring_responses/yielding.feature @@ -0,0 +1,76 @@ +Feature: Yielding + + Use `and_yield` to make the test double yield the provided arguments when it receives the + message. If the caller does not provide a block, or the caller's block does not accept the + provided arguments, an error will be raised. If you want to yield multiple times, chain + multiple `and_yield` calls together. + + Scenario: Yield an argument + Given a file named "yield_arguments_spec.rb" with: + """ruby + RSpec.describe "Making it yield arguments" do + it "yields the provided args" do + dbl = double + allow(dbl).to receive(:foo).and_yield(2, 3) + + x = y = nil + dbl.foo { |a, b| x, y = a, b } + expect(x).to eq(2) + expect(y).to eq(3) + end + end + """ + When I run `rspec yield_arguments_spec.rb` + Then the examples should all pass + + Scenario: It fails when the caller does not provide a block + Given a file named "no_caller_block_spec.rb" with: + """ruby + RSpec.describe "Making it yield" do + it "fails when the caller does not provide a block" do + dbl = double + allow(dbl).to receive(:foo).and_yield(2, 3) + dbl.foo + end + end + """ + When I run `rspec no_caller_block_spec.rb` + Then it should fail with: + """ + Double asked to yield |[2, 3]| but no block was passed + """ + + Scenario: It fails when the caller's block does not accept the provided arguments + Given a file named "arg_mismatch_spec.rb" with: + """ruby + RSpec.describe "Making it yield" do + it "fails when the caller's block does not accept the provided arguments" do + dbl = double + allow(dbl).to receive(:foo).and_yield(2, 3) + dbl.foo { |x| } + end + end + """ + When I run `rspec arg_mismatch_spec.rb` + Then it should fail with: + """ + Double yielded |2, 3| to block with arity of 1 + """ + + Scenario: Yield multiple times + Given a file named "yield_multiple_times_spec.rb" with: + """ + RSpec.describe "Making it yield multiple times" do + it "yields the specified args in succession" do + yielded = [] + + dbl = double + allow(dbl).to receive(:foo).and_yield(1).and_yield(2).and_yield(3) + dbl.foo { |x| yielded << x } + + expect(yielded).to eq([1, 2, 3]) + end + end + """ + When I run `rspec yield_multiple_times_spec.rb` + Then the examples should all pass diff --git a/features/message_expectations/README.md b/features/message_expectations/README.md deleted file mode 100644 index b0015c41e..000000000 --- a/features/message_expectations/README.md +++ /dev/null @@ -1,75 +0,0 @@ -### Basics - - # create a double - obj = double() - - # expect a message - expect(obj).to receive(:message) - - # specify a return value - expect(obj).to receive(:message) { :value } - expect(obj).to receive(:message).and_return(:value) - - # specify multiple message/return value pairs - expect(obj).to receive_messages(:message => :value, :another_message => :another_value) - -These forms are somewhat interchangeable. The difference is that the -block contents are evaluated lazily when the `obj` receives the -`message` message, whereas the others are evaluated as they are read. - -### Fake implementation - - expect(obj).to receive(:message) do |arg1, arg2| - # set expectations about the args in this block - # and set a return value - end - -### Using the original implementation - - expect(obj).to receive(:message).and_call_original - -### Raising/Throwing - - expect(obj).to receive(:message).and_raise("this error") - expect(obj).to receive(:message).and_throw(:this_symbol) - -You can also use the block format: - - expect(obj).to receive(:message) { raise "this error" } - expect(obj).to receive(:message) { throw :this_symbol } - -### Argument constraints - -#### Explicit arguments - - expect(obj).to receive(:message).with('an argument') - expect(obj).to receive(:message).with('more_than', 'one_argument') - -#### Argument matchers - - expect(obj).to receive(:message).with(anything()) - expect(obj).to receive(:message).with(an_instance_of(Money)) - expect(obj).to receive(:message).with(hash_including(:a => 'b')) - -#### Regular expressions - - expect(obj).to receive(:message).with(/abc/) - -### Counts - - expect(obj).to receive(:message).once - expect(obj).to receive(:message).twice - expect(obj).to receive(:message).exactly(3).times - - expect(obj).to receive(:message).at_least(:once) - expect(obj).to receive(:message).at_least(:twice) - expect(obj).to receive(:message).at_least(n).times - - expect(obj).to receive(:message).at_most(:once) - expect(obj).to receive(:message).at_most(:twice) - expect(obj).to receive(:message).at_most(n).times - -### Ordering - - expect(obj).to receive(:one).ordered - expect(obj).to receive(:two).ordered diff --git a/features/message_expectations/allow_any_instance_of.feature b/features/message_expectations/allow_any_instance_of.feature deleted file mode 100644 index 0227018ce..000000000 --- a/features/message_expectations/allow_any_instance_of.feature +++ /dev/null @@ -1,26 +0,0 @@ -Feature: allow a message on any instance of a class - - Use `allow_any_instance_of(Class).to receive` when you want to configure how - instances of the given class respond to a message without setting an - expectation that the message will be received. - - Scenario: allowing a message on any instance of a class - Given a file named "example_spec.rb" with: - """ruby - describe "any_instance.to receive" do - before do - allow_any_instance_of(Object).to receive(:foo).and_return(:return_value) - end - - it "allows any instance of the class to receive the message" do - o = Object.new - expect(o.foo).to eq(:return_value) - end - - it "passes even if no instances receive that message" do - o = Object.new - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass diff --git a/features/message_expectations/any_instance.feature b/features/message_expectations/any_instance.feature deleted file mode 100644 index eefe87aac..000000000 --- a/features/message_expectations/any_instance.feature +++ /dev/null @@ -1,43 +0,0 @@ -Feature: expect a message on any instance of a class - - Use `expect_any_instance_of(Class).to receive` to set an expectation that one - (and only one) instance of a class receives a message before the example is - completed. - - The example will fail if no instance receives the specified message. - - Scenario: expect a message on any instance of a class - Given a file named "example_spec.rb" with: - """ruby - describe "expect_any_instance_of" do - before do - expect_any_instance_of(Object).to receive(:foo).and_return(:return_value) - end - - it "verifies that one instance of the class receives the message" do - o = Object.new - expect(o.foo).to eq(:return_value) - end - - it "fails unless an instance receives that message" do - end - end - """ - When I run `rspec example_spec.rb` - Then the output should contain "2 examples, 1 failure" - And the output should contain "1) expect_any_instance_of fails unless an instance receives that message" - - Scenario: expect a message on any instance of a class (should syntax) - Given a file named "example_spec.rb" with: - """ruby - describe "any_instance.should_receive" do - it "verifies that one instance of the class receives the message" do - Object.any_instance.should_receive(:foo).and_return(:return_value) - - o = Object.new - expect(o.foo).to eq(:return_value) - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass diff --git a/features/message_expectations/block_local_expectations.feature.pending b/features/message_expectations/block_local_expectations.feature.pending deleted file mode 100644 index 40fb7bd7c..000000000 --- a/features/message_expectations/block_local_expectations.feature.pending +++ /dev/null @@ -1,55 +0,0 @@ -Feature: block local expectations - - Background: - Given a file named "lib/account.rb" with: - """ - class Account - def self.create - yield new - end - - def opening_balance(amount, currency) - end - end - """ - - Scenario: passing example - Given a file named "spec/account_spec.rb" with: - """ - require 'account' - - describe "account DSL" do - it "it succeeds when the block local receives the given call" do - account = double("Account") - Account.should_receive(:create).and_yield(account) do |account| - account.should_receive(:opening_balance).with(100, :USD) - end - Account.create do |account| - account.opening_balance 100, :USD - end - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: failing example - - Given a file named "spec/account_spec.rb" with: - """ - require 'account' - - describe "account DSL" do - it "fails when the block local does not receive the expected call" do - Account.should_receive(:create).and_yield do |account| - account.should_receive(:opening_balance).with(100, :USD) - end - Account.create do |account| - # opening_balance is not called here - end - end - end - """ - - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 1 failure" \ No newline at end of file diff --git a/features/message_expectations/call_original.feature b/features/message_expectations/call_original.feature deleted file mode 100644 index 7c26d3d76..000000000 --- a/features/message_expectations/call_original.feature +++ /dev/null @@ -1,23 +0,0 @@ -Feature: Calling the original method - - You can use `and_call_original` on the fluent interface - to "pass through" the received message to the original method. - - Scenario: expect a message - Given a file named "call_original_spec.rb" with: - """ruby - class Addition - def self.two_plus_two - 4 - end - end - - describe "and_call_original" do - it "delegates the message to the original implementation" do - expect(Addition).to receive(:two_plus_two).and_call_original - expect(Addition.two_plus_two).to eq(4) - end - end - """ - When I run `rspec call_original_spec.rb` - Then the examples should all pass diff --git a/features/message_expectations/expect_message_using_expect.feature b/features/message_expectations/expect_message_using_expect.feature deleted file mode 100644 index 2789bc68d..000000000 --- a/features/message_expectations/expect_message_using_expect.feature +++ /dev/null @@ -1,107 +0,0 @@ -Feature: expect message using `expect` - - Use `expect(object).to receive(:message)` to set an expectation that - `object` should receive the message `:message` before the example is - completed. - - Note: You can use `expect_any_instance_of` when you don't have a reference - to the object that receives a message in your test. For more information, - see the message_expectations/expect_any_instance_of feature. - - Scenario: expect a message - Given a file named "spec/account_spec.rb" with: - """ruby - require "account" - - describe Account do - context "when closed" do - it "logs an account closed message" do - logger = double("logger") - account = Account.new logger - - expect(logger).to receive(:account_closed) - - account.close - end - end - end - """ - And a file named "lib/account.rb" with: - """ruby - Account = Struct.new(:logger) do - def close - logger.account_closed - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: expect a message with an argument - Given a file named "spec/account_spec.rb" with: - """ruby - require "account" - - describe Account do - context "when closed" do - it "logs an account closed message" do - logger = double("logger") - account = Account.new logger - - expect(logger).to receive(:account_closed).with(account) - - account.close - end - end - end - """ - And a file named "lib/account.rb" with: - """ruby - Account = Struct.new(:logger) do - def close - logger.account_closed(self) - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: provide a return value - Given a file named "spec/message_expectation_spec.rb" with: - """ruby - - describe "a message expectation with a return value" do - context "specified in a block" do - it "returns the specified value" do - object = double("object") - expect(object).to receive(:message) { :return_value } - expect(object.message).to eq(:return_value) - end - end - - context "specified with and_return" do - it "returns the specified value" do - object = double("object") - expect(object).to receive(:message).and_return(:return_value) - expect(object.message).to eq(:return_value) - end - end - end - """ - When I run `rspec spec/message_expectation_spec.rb` - Then the output should contain "2 examples, 0 failures" - - Scenario: expect a specific number of calls - Given a file named "spec/message_count_spec.rb" with: - """ruby - describe "a message expectation with a count" do - it "passes if the expected number of calls happen" do - string = "hi" - expect(string).to receive(:length).exactly(3).times - - 3.times { string.length } - end - end - """ - When I run `rspec spec/message_count_spec.rb` - Then the output should contain "1 example, 0 failures" diff --git a/features/message_expectations/expect_message_using_should_receive.feature b/features/message_expectations/expect_message_using_should_receive.feature deleted file mode 100644 index 6ff9aed83..000000000 --- a/features/message_expectations/expect_message_using_should_receive.feature +++ /dev/null @@ -1,118 +0,0 @@ -Feature: expect message using `should_receive` - - Use `object.should_receive(:message)` to set an expectation that - `object` should receive the message `:message` before the example is - completed. - - Background: - Given a file named "spec/spec_helper.rb" with: - """ruby - RSpec.configure do |config| - config.mock_with :rspec do |mocks| - mocks.syntax = :should - end - end - """ - - Scenario: expect a message - Given a file named "spec/account_spec.rb" with: - """ruby - require "account" - require "spec_helper" - - describe Account do - context "when closed" do - it "logs an account closed message" do - logger = double("logger") - account = Account.new logger - - logger.should_receive(:account_closed) - - account.close - end - end - end - """ - And a file named "lib/account.rb" with: - """ruby - Account = Struct.new(:logger) do - def close - logger.account_closed - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: expect a message with an argument - Given a file named "spec/account_spec.rb" with: - """ruby - require "account" - require "spec_helper" - - describe Account do - context "when closed" do - it "logs an account closed message" do - logger = double("logger") - account = Account.new logger - - logger.should_receive(:account_closed).with(account) - - account.close - end - end - end - """ - And a file named "lib/account.rb" with: - """ruby - Account = Struct.new(:logger) do - def close - logger.account_closed(self) - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: provide a return value - Given a file named "spec/message_expectation_spec.rb" with: - """ruby - require "spec_helper" - - describe "a message expectation with a return value" do - context "specified in a block" do - it "returns the specified value" do - object = double("object") - object.should_receive(:message) { :return_value } - expect(object.message).to eq(:return_value) - end - end - - context "specified with and_return" do - it "returns the specified value" do - object = double("object") - object.should_receive(:message).and_return(:return_value) - expect(object.message).to eq(:return_value) - end - end - end - """ - When I run `rspec spec/message_expectation_spec.rb` - Then the output should contain "2 examples, 0 failures" - - Scenario: expect a specific number of calls - Given a file named "spec/message_count_spec.rb" with: - """ruby - require "spec_helper" - - describe "a message expectation with a count" do - it "passes if the expected number of calls happen" do - string = "hi" - string.should_receive(:length).exactly(3).times - - 3.times { string.length } - end - end - """ - When I run `rspec spec/message_count_spec.rb` - Then the output should contain "1 example, 0 failures" diff --git a/features/message_expectations/message_chains_using_expect.feature b/features/message_expectations/message_chains_using_expect.feature deleted file mode 100644 index 2beb9e3d8..000000000 --- a/features/message_expectations/message_chains_using_expect.feature +++ /dev/null @@ -1,49 +0,0 @@ -Feature: Message chains in the expect syntax - - You can use `receive_message_chain` to stub nested calls - on both partial and pure mock objects. - - Scenario: allow a chained message - Given a file named "spec/chained_messages.rb" with: - """ruby - describe "a chained message expectation" do - it "passes if the expected number of calls happen" do - d = double - allow(d).to receive_message_chain(:to_a, :length) - - d.to_a.length - end - end - """ - When I run `rspec spec/chained_messages.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: allow a chained message with a return value - Given a file named "spec/chained_messages.rb" with: - """ruby - describe "a chained message expectation" do - it "passes if the expected number of calls happen" do - d = double - allow(d).to receive_message_chain(:to_a, :length).and_return(3) - - expect(d.to_a.length).to eq(3) - end - end - """ - When I run `rspec spec/chained_messages.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: expect a chained message with a return value - Given a file named "spec/chained_messages.rb" with: - """ruby - describe "a chained message expectation" do - it "passes if the expected number of calls happen" do - d = double - expect(d).to receive_message_chain(:to_a, :length).and_return(3) - - expect(d.to_a.length).to eq(3) - end - end - """ - When I run `rspec spec/chained_messages.rb` - Then the output should contain "1 example, 0 failures" diff --git a/features/message_expectations/receive_counts.feature b/features/message_expectations/receive_counts.feature deleted file mode 100644 index f8573593b..000000000 --- a/features/message_expectations/receive_counts.feature +++ /dev/null @@ -1,209 +0,0 @@ -Feature: receive counts - - Scenario: expect a message once - Given a file named "spec/account_spec.rb" with: - """ruby - class Account - attr_accessor :logger - - def open - logger.account_opened - end - end - - describe Account do - context "when opened" do - it "logger#account_opened was called once" do - logger = double("logger") - account = Account.new - account.logger = logger - - expect(logger).to receive(:account_opened).once - - account.open - end - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: expect a message twice - Given a file named "spec/account_spec.rb" with: - """ruby - class Account - attr_accessor :logger - - def open - logger.account_opened - end - end - - describe Account do - context "when opened" do - it "logger#account_opened was called once" do - logger = double("logger") - account = Account.new - account.logger = logger - - expect(logger).to receive(:account_opened).twice - - account.open - account.open - end - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: expect a message 3 times - Given a file named "spec/account_spec.rb" with: - """ruby - class Account - attr_accessor :logger - - def open - logger.account_opened - end - end - - describe Account do - context "when opened" do - it "logger#account_opened was called once" do - logger = double("logger") - account = Account.new - account.logger = logger - - expect(logger).to receive(:account_opened).exactly(3).times - - account.open - account.open - account.open - end - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: expect a message at least (:once) - Given a file named "spec/account_spec.rb" with: - """ruby - class Account - attr_accessor :logger - - def open - logger.account_opened - end - end - - describe Account do - context "when opened" do - it "logger#account_opened was called once" do - logger = double("logger") - account = Account.new - account.logger = logger - - expect(logger).to receive(:account_opened).at_least(:once) - - account.open - end - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - - Scenario: expect a message at least (n) times - Given a file named "spec/account_spec.rb" with: - """ruby - class Account - attr_accessor :logger - - def open - logger.account_opened - end - end - - describe Account do - context "when opened" do - it "logger#account_opened was called once" do - logger = double("logger") - account = Account.new - account.logger = logger - - expect(logger).to receive(:account_opened).at_least(3).times - - # Note that I am calling method under test 4 times - # and I specified it to be called at least 3 times - account.open - account.open - account.open - account.open - end - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: expect a message at most (:once) - Given a file named "spec/account_spec.rb" with: - """ruby - class Account - attr_accessor :logger - - def open - logger.account_opened - end - end - - describe Account do - context "when opened" do - it "logger#account_opened was called once" do - logger = double("logger") - account = Account.new - account.logger = logger - - expect(logger).to receive(:account_opened).at_most(:once) - - account.open - account.open - end - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "expected: at most 1 time" - And the output should contain "received: 2 times" - - Scenario: expect a message at most (n) times - Given a file named "spec/account_spec.rb" with: - """ruby - class Account - attr_accessor :logger - - def open - logger.account_opened - end - end - - describe Account do - context "when opened" do - it "logger#account_opened was called once" do - logger = double("logger") - account = Account.new - account.logger = logger - - expect(logger).to receive(:account_opened).at_most(2).times - - account.open - account.open - end - end - end - """ - When I run `rspec spec/account_spec.rb` - Then the output should contain "1 example, 0 failures" diff --git a/features/message_expectations/warn_when_expectation_is_set_on_nil.feature b/features/message_expectations/warn_when_expectation_is_set_on_nil.feature deleted file mode 100644 index 3e905ed9b..000000000 --- a/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +++ /dev/null @@ -1,50 +0,0 @@ -Feature: warn when expectation is set on nil - - Scenario: nil instance variable - Given a file named "example_spec.rb" with: - """ruby - RSpec.configure {|c| c.mock_with :rspec} - describe "something" do - it "does something" do - expect(@i_do_not_exist).to receive(:foo) - @i_do_not_exist.foo - end - end - """ - When I run `rspec example_spec.rb` - Then the output should contain "An expectation of :foo was set on nil" - - Scenario: allow - Given a file named "example_spec.rb" with: - """ruby - RSpec.configure {|c| c.mock_with :rspec} - describe "something" do - it "does something" do - allow_message_expectations_on_nil - expect(nil).to receive(:foo) - nil.foo - end - end - """ - When I run `rspec example_spec.rb` - Then the output should not contain "An expectation" - - Scenario: allow in one example, but not on another - Given a file named "example_spec.rb" with: - """ruby - RSpec.configure {|c| c.mock_with :rspec} - describe "something" do - it "does something (foo)" do - allow_message_expectations_on_nil - nil.should_receive(:foo) - nil.foo - end - it "does something (bar)" do - expect(nil).to receive(:bar) - nil.bar - end - end - """ - When I run `rspec example_spec.rb` - Then the output should contain "An expectation of :bar" - And the output should not contain "An expectation of :foo" diff --git a/features/method_stubs/README.md b/features/method_stubs/README.md deleted file mode 100644 index 383190687..000000000 --- a/features/method_stubs/README.md +++ /dev/null @@ -1,77 +0,0 @@ -### Stub return values - - # create a double - obj = double() - - # specify a return value using `:expect` syntax - allow(obj).to receive(:message) { :value } - allow(obj).to receive(:message).and_return(:value) - - # specify a return value using `:should` syntax - obj.stub(:message) { :value } - obj.stub(:message => :value) - obj.stub(:message).and_return(:value) - -These forms are somewhat interchangeable. The difference is that the -block contents are evaluated lazily when the `obj` receives the -`message` message, whereas the others are evaluated as they are read. - -### Fake implementation - - allow(obj).to receive(:message) do |arg1, arg2| - # set expectations about the args in this block - # and/or return value - end - - obj.stub(:message) do |arg1, arg2| - # set expectations about the args in this block - # and/or return a value - end - -### Raising/Throwing - - allow(obj).to receive(:message).and_raise("this error") - allow(obj).to receive(:message).and_throw(:this_symbol) - - obj.stub(:message).and_raise("this error") - obj.stub(:message).and_throw(:this_symbol) - -You can also use the block format: - - allow(obj).to receive(:message) { raise "this error" } - allow(obj).to receive(:message) { throw :this_symbol } - - obj.stub(:message) { raise "this error" } - obj.stub(:message) { throw :this_symbol } - -### Argument constraints - -#### Explicit arguments - - allow(obj).to receive(:message).with('an argument') { ... } - allow(obj).to receive(:message).with('more than', 'an argument') { ... } - - obj.stub(:message).with('an argument') { ... } - obj.stub(:message).with('more_than', 'one_argument') { ... } - -#### Argument matchers - - allow(obj).to receive(:message).with(anything()) { ... } - allow(obj).to receive(:message).with(an_instance_of(Money)) { ... } - allow(obj).to receive(:message).with(hash_including(:a => 'b')) { ... } - allow(obj).to receive(:message).with(array_including(1,2,3)) { ... } - # or - allow(obj).to receive(:message).with(array_including([1,2,3])) { ... } - - obj.stub(:message).with(anything()) { ... } - obj.stub(:message).with(an_instance_of(Money)) { ... } - obj.stub(:message).with(hash_including(:a => 'b')) { ... } - obj.stub(:message).with(array_including(1,2,3)) { ... } - # or - obj.stub(:message).with(array_including([1,2,3])) { ... } - -#### Regular expressions - - allow(obj).to receive(:message).with(/abc/) { ... } - - obj.stub(:message).with(/abc/) { ... } diff --git a/features/method_stubs/allow_any_instance_of.feature b/features/method_stubs/allow_any_instance_of.feature deleted file mode 100644 index 684642776..000000000 --- a/features/method_stubs/allow_any_instance_of.feature +++ /dev/null @@ -1,136 +0,0 @@ -Feature: stub on any instance of a class - - Use `allow_any_instance_of` on a class to tell any instance of that class to - return a value (or values) in response to a given message. If no instance - receives the message, nothing happens. - - Messages can be stubbed on any class, including those in Ruby's core library. - - Note: You can use `allow_any_instance_of` when you don't have a reference - to the object that receives a message in your test. For more information, - see the message_expectations/allow_any_instance_of feature. - - Scenario: Stubbing any instance of an object with a single return value - Given a file named "example_spec.rb" with: - """ruby - describe "stubbing any instance" do - it "returns the specified value on any instance of the class" do - allow_any_instance_of(Object).to receive(:foo).and_return(:return_value) - - o = Object.new - expect(o.foo).to eq(:return_value) - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass - - Scenario: any_instance stub with a hash - Given a file named "example_spec.rb" with: - """ruby - describe "any_instance.stub" do - context "with a hash" do - it "returns the hash values on any instance of the class" do - allow_any_instance_of(Object).to receive_messages(:foo => 'foo', :bar => 'bar') - - o = Object.new - expect(o.foo).to eq('foo') - expect(o.bar).to eq('bar') - end - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass - - Scenario: Stubbing any instance of an object with specific arguments matchers - Given a file named "example_spec.rb" with: - """ruby - describe "stubbing any instance" do - context "with arguments" do - it "returns the stubbed value when arguments match" do - allow_any_instance_of(Object).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) - allow_any_instance_of(Object).to receive(:foo).with(:param_three, :param_four).and_return(:result_two) - - o = Object.new - expect(o.foo(:param_one, :param_two)).to eq(:result_one) - expect(o.foo(:param_three, :param_four)).to eq(:result_two) - end - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass - - Scenario: any_instance unstub - Given a file named "example_spec.rb" with: - """ruby - describe "any_instance.unstub" do - it "unstubs a stubbed method" do - class Object - def foo - :foo - end - end - - Object.any_instance.stub(:foo) - Object.any_instance.unstub(:foo) - - expect(Object.new.foo).to eq(:foo) - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass - - Scenario: any_instance unstub - Given a file named "example_spec.rb" with: - """ruby - describe "any_instance.unstub" do - context "with both an expectation and a stub already set" do - it "only removes the stub" do - class Object - def foo - :foo - end - end - expect_any_instance_of(Object).to receive(:foo).and_return(:bar) - Object.any_instance.stub(:foo) - Object.any_instance.unstub(:foo) - - expect(Object.new.foo).to eq(:bar) - end - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass - - Scenario: stub a chain of methods an any instance - Given a file named "stub_chain_spec.rb" with: - """ruby - describe "stubbing a chain of methods" do - context "given symbols representing methods" do - it "returns the correct value" do - allow_any_instance_of(Object).to receive_message_chain(:one, :two, :three).and_return(:four) - expect(Object.new.one.two.three).to eq(:four) - end - end - - context "given a hash at the end" do - it "returns the correct value" do - allow_any_instance_of(Object).to receive_message_chain(:one, :two, :three=> :four) - expect(Object.new.one.two.three).to eq(:four) - end - end - - context "given a string of methods separated by dots" do - it "returns the correct value" do - allow_any_instance_of(Object).to receive_message_chain("one.two.three").and_return(:four) - expect(Object.new.one.two.three).to eq(:four) - end - end - end - """ - When I run `rspec stub_chain_spec.rb` - Then the examples should all pass diff --git a/features/method_stubs/as_null_object.feature b/features/method_stubs/as_null_object.feature deleted file mode 100644 index 9a4b7bdcd..000000000 --- a/features/method_stubs/as_null_object.feature +++ /dev/null @@ -1,40 +0,0 @@ -Feature: as_null_object - - Use the `as_null_object` method to ignore any messages that aren't explicitly - set as stubs or message expectations. - - EXCEPTION: `to_ary` will raise a NoMethodError unless explicitly stubbed in - order to support `flatten` on an Array containing a double. - - Scenario: double acting as_null_object - Given a file named "as_null_object_spec.rb" with: - """ruby - describe "a double with as_null_object called" do - let(:null_object) { double('null object').as_null_object } - - it "responds to any method that is not defined" do - expect(null_object).to respond_to(:an_undefined_method) - end - - it "allows explicit stubs using expect syntax" do - allow(null_object).to receive(:foo) { "bar" } - expect(null_object.foo).to eq("bar") - end - - it "allows explicit stubs using should syntax" do - null_object.stub(:foo) { "bar" } - expect(null_object.foo).to eq("bar") - end - - it "allows explicit expectations" do - expect(null_object).to receive(:something) - null_object.something - end - - it "supports Array#flatten" do - expect([null_object].flatten).to eq([null_object]) - end - end - """ - When I run `rspec as_null_object_spec.rb` - Then the examples should all pass diff --git a/features/method_stubs/receive_message_chain.feature b/features/method_stubs/receive_message_chain.feature deleted file mode 100644 index aa298dc21..000000000 --- a/features/method_stubs/receive_message_chain.feature +++ /dev/null @@ -1,51 +0,0 @@ -Feature: stub a chain of methods - - Use the `stub_chain` method to stub a chain of two or more methods in one - statement. Method chains are considered a design smell, but it's not really - the method chain itself that is the problem - it's the dependency chain - represented by a chain of messages to different objects: - - foo.get_bar.get_baz - - This is a Law of Demeter violation if `get_bar` returns an object other than - `foo`, and `get_baz` returns yet another object. - - Fluent interfaces look similar from a caller's perspective, but don't - represent a dependency chain (the caller depends only on the object it is - calling). Consider this common example from Ruby on Rails: - - Article.recent.by(current_user) - - The `recent` and `by` methods return the same object, so this is not a Law of - Demeter violation. - - Scenario: stub a chain of methods - Given a file named "stub_chain_spec.rb" with: - """ruby - describe "stubbing a chain of methods" do - subject { Object.new } - - context "given symbols representing methods" do - it "returns the correct value" do - allow(subject).to receive_message_chain(:one, :two, :three).and_return(:four) - expect(subject.one.two.three).to eq(:four) - end - end - - context "given a hash at the end" do - it "returns the correct value" do - allow(subject).to receive_message_chain(:one, :two, :three => :four) - expect(subject.one.two.three).to eq(:four) - end - end - - context "given a string of methods separated by dots" do - it "returns the correct value" do - allow(subject).to receive_message_chain("one.two.three").and_return(:four) - expect(subject.one.two.three).to eq(:four) - end - end - end - """ - When I run `rspec stub_chain_spec.rb` - Then the examples should all pass diff --git a/features/method_stubs/simple_return_value_with_allow.feature b/features/method_stubs/simple_return_value_with_allow.feature deleted file mode 100644 index 82fca7500..000000000 --- a/features/method_stubs/simple_return_value_with_allow.feature +++ /dev/null @@ -1,44 +0,0 @@ -Feature: allow with a simple return value - - Use the `allow` method with the `receive` matcher on a test double or a real - object to tell the object to return a value (or values) in response to a given - message. Nothing happens if the message is never received. - - Scenario: stub with no return value - Given a file named "example_spec.rb" with: - """ruby - describe "a stub with no return value specified" do - let(:collaborator) { double("collaborator") } - - it "returns nil" do - allow(collaborator).to receive(:message) - expect(collaborator.message).to be(nil) - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass - - Scenario: stubs with return values - Given a file named "example_spec.rb" with: - """ruby - describe "a stub with a return value" do - context "specified in a block" do - it "returns the specified value" do - collaborator = double("collaborator") - allow(collaborator).to receive(:message) { :value } - expect(collaborator.message).to eq(:value) - end - end - - context "specified with #and_return" do - it "returns the specified value" do - collaborator = double("collaborator") - allow(collaborator).to receive(:message).and_return(:value) - expect(collaborator.message).to eq(:value) - end - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass diff --git a/features/method_stubs/simple_return_value_with_stub.feature b/features/method_stubs/simple_return_value_with_stub.feature deleted file mode 100644 index 33e2918e6..000000000 --- a/features/method_stubs/simple_return_value_with_stub.feature +++ /dev/null @@ -1,64 +0,0 @@ -Feature: stub with a simple return value - - Use the `stub` method on a test double or a real object to tell the object to - return a value (or values) in response to a given message. Nothing happens if - the message is never received. - - Scenario: stub with no return value - Given a file named "example_spec.rb" with: - """ruby - describe "a stub with no return value specified" do - let(:collaborator) { double("collaborator") } - - it "returns nil" do - collaborator.stub(:message) - expect(collaborator.message).to be(nil) - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass - - Scenario: stubs with return values - Given a file named "example_spec.rb" with: - """ruby - describe "a stub with a return value" do - context "specified in a block" do - it "returns the specified value" do - collaborator = double("collaborator") - collaborator.stub(:message) { :value } - expect(collaborator.message).to eq(:value) - end - end - - context "specified with #and_return" do - it "returns the specified value" do - collaborator = double("collaborator") - collaborator.stub(:message).and_return(:value) - expect(collaborator.message).to eq(:value) - end - end - - context "specified with a hash passed to #stub" do - it "returns the specified value" do - collaborator = double("collaborator") - collaborator.stub(:message_1 => :value_1, :message_2 => :value_2) - expect(collaborator.message_1).to eq(:value_1) - expect(collaborator.message_2).to eq(:value_2) - end - end - - context "specified with a hash passed to #double" do - it "returns the specified value" do - collaborator = double("collaborator", - :message_1 => :value_1, - :message_2 => :value_2 - ) - expect(collaborator.message_1).to eq(:value_1) - expect(collaborator.message_2).to eq(:value_2) - end - end - end - """ - When I run `rspec example_spec.rb` - Then the examples should all pass diff --git a/features/method_stubs/stub_implementation.feature b/features/method_stubs/stub_implementation.feature deleted file mode 100644 index ebba88c54..000000000 --- a/features/method_stubs/stub_implementation.feature +++ /dev/null @@ -1,48 +0,0 @@ -Feature: stub with substitute implementation - - You can stub an implementation of a method (a.k.a. fake) by passing a block - to the `stub` method. - - Scenario: stub implementation using `expect` syntax - Given a file named "stub_implementation_spec.rb" with: - """ruby - describe "a stubbed implementation" do - it "works" do - object = Object.new - allow(object).to receive(:foo) do |arg| - if arg == :this - "got this" - elsif arg == :that - "got that" - end - end - - expect(object.foo(:this)).to eq("got this") - expect(object.foo(:that)).to eq("got that") - end - end - """ - When I run `rspec stub_implementation_spec.rb` - Then the output should contain "1 example, 0 failures" - - Scenario: stub implementation using `should` syntax - Given a file named "stub_implementation_spec.rb" with: - """ruby - describe "a stubbed implementation" do - it "works" do - object = Object.new - object.stub(:foo) do |arg| - if arg == :this - "got this" - elsif arg == :that - "got that" - end - end - - expect(object.foo(:this)).to eq("got this") - expect(object.foo(:that)).to eq("got that") - end - end - """ - When I run `rspec stub_implementation_spec.rb` - Then the output should contain "1 example, 0 failures" diff --git a/features/method_stubs/to_ary.feature b/features/method_stubs/to_ary.feature deleted file mode 100644 index 6d8327b68..000000000 --- a/features/method_stubs/to_ary.feature +++ /dev/null @@ -1,51 +0,0 @@ -Feature: double handling to_ary - - Ruby implicitly sends `to_ary` to all of the objects in an `Array` when the - array receives `flatten`: - - [obj].flatten # => obj.to_ary - - If `to_ary` raises a `NoMethodError`, Ruby sees that the object can not be coerced - into an array and moves on to the next object. - - To support this, an RSpec double will raise a NoMethodError when it receives - `to_ary` _even if it is set `as_null_object`_, unless `to_ary` is explicitly - stubbed. - - Scenario: double receiving to_ary - Given a file named "example.rb" with: - """ruby - describe "#to_ary" do - shared_examples "to_ary" do - it "can be overridden with a stub" do - allow(obj).to receive(:to_ary) { :non_nil_value } - expect(obj.to_ary).to be(:non_nil_value) - end - - it "supports Array#flatten" do - obj = double('foo') - expect([obj].flatten).to eq([obj]) - end - end - - context "sent to a double as_null_object" do - let(:obj) { double('obj').as_null_object } - include_examples "to_ary" - - it "returns nil" do - expect( obj.to_ary ).to eq nil - end - end - - context "sent to a double without as_null_object" do - let(:obj) { double('obj') } - include_examples "to_ary" - - it "raises a NoMethodError" do - expect { obj.to_ary }.to raise_error(NoMethodError) - end - end - end - """ - When I run `rspec example.rb` - Then the examples should all pass diff --git a/features/mutating_constants/README.md b/features/mutating_constants/README.md index 024e82f55..1d92a8684 100644 --- a/features/mutating_constants/README.md +++ b/features/mutating_constants/README.md @@ -1,18 +1,15 @@ -## Mutating Constants - ### Stubbing -Support is provided for stubbing constants. Like with method stubs, the -stubbed constants will be restored to their original state when an -example completes. +Support is provided for stubbing constants. Like with method stubs, the stubbed constants +will be restored to their original state when an example completes. ``` ruby stub_const("Foo", fake_foo) Foo # => fake_foo ``` -Stubbed constant names must be fully qualified; the current module -nesting is not considered. +Stubbed constant names must be fully qualified; the current module nesting is not +considered. ``` ruby module MyGem @@ -36,10 +33,9 @@ module MyGem end ``` -When you stub a constant that is a module or a class, nested -constants on the original module or class are not transferred -by default, but you can use the `:transfer_nested_constants` -option to tell rspec-mocks to transfer them: +When you stub a constant that is a module or a class, nested constants on the original +module or class are not transferred by default, but you can use the +`:transfer_nested_constants` option to tell rspec-mocks to transfer them: ``` ruby class CardDeck @@ -64,8 +60,8 @@ CardDeck::NUM_CARDS # => raises uninitialized constant error ### Hiding -Support is also provided for hiding constants. Hiding a constant temporarily -removes it; it is restored to its original value after the test completes. +Support is also provided for hiding constants. Hiding a constant temporarily removes it; it is +restored to its original value after the test completes. ```ruby FOO = 42 diff --git a/features/mutating_constants/hiding_defined_constant.feature b/features/mutating_constants/hide_defined_constant.feature similarity index 92% rename from features/mutating_constants/hiding_defined_constant.feature rename to features/mutating_constants/hide_defined_constant.feature index 9748394a1..559542b1a 100644 --- a/features/mutating_constants/hiding_defined_constant.feature +++ b/features/mutating_constants/hide_defined_constant.feature @@ -7,7 +7,7 @@ Feature: Hide Defined Constant """ruby FOO = 7 - describe "hiding FOO" do + RSpec.describe "hiding FOO" do it "can hide FOO" do hide_const("FOO") expect { FOO }.to raise_error(NameError) @@ -31,7 +31,7 @@ Feature: Hide Defined Constant end module MyGem - describe SomeClass do + RSpec.describe SomeClass do it "hides the nested constant when it is fully qualified" do hide_const("MyGem::SomeClass::FOO") expect { SomeClass::FOO }.to raise_error(NameError) @@ -48,8 +48,8 @@ Feature: Hide Defined Constant Scenario: Hiding undefined constant Given a file named "hide_const_spec.rb" with: - """ - describe "hiding UNDEFINED_CONSTANT" do + """ruby + RSpec.describe "hiding UNDEFINED_CONSTANT" do it "has no effect" do hide_const("UNDEFINED_CONSTANT") expect { UNDEFINED_CONSTANT }.to raise_error(NameError) diff --git a/features/mutating_constants/hide_undefined_constant.feature b/features/mutating_constants/hide_undefined_constant.feature new file mode 100644 index 000000000..629ab2379 --- /dev/null +++ b/features/mutating_constants/hide_undefined_constant.feature @@ -0,0 +1,22 @@ +Feature: Hide Undefined Constant + + Hiding a constant that is already undefined is a no-op. This can be useful when a spec file + may run in either an isolated environment (e.g. when running one spec file) or in a full + environment with all parts of your code base loaded (e.g. when running your entire suite). + + Scenario: Hiding undefined constant + Given a file named "hide_const_spec.rb" with: + """ruby + RSpec.describe "hiding UNDEFINED_CONSTANT" do + it "has no effect" do + hide_const("UNDEFINED_CONSTANT") + expect { UNDEFINED_CONSTANT }.to raise_error(NameError) + end + + it "is still undefined after the example completes" do + expect { UNDEFINED_CONSTANT }.to raise_error(NameError) + end + end + """ + When I run `rspec hide_const_spec.rb` + Then the examples should all pass diff --git a/features/mutating_constants/stub_defined_constant.feature b/features/mutating_constants/stub_defined_constant.feature index ddd0238db..4d61e38c8 100644 --- a/features/mutating_constants/stub_defined_constant.feature +++ b/features/mutating_constants/stub_defined_constant.feature @@ -1,15 +1,14 @@ Feature: Stub Defined Constant - Use `stub_const` to stub constants. When the constant is already defined, - the stubbed value will replace the original value for the duration of the - example. + Use `stub_const` to stub constants. When the constant is already defined, the stubbed value + will replace the original value for the duration of the example. Scenario: Stub top-level constant Given a file named "stub_const_spec.rb" with: """ruby FOO = 7 - describe "stubbing FOO" do + RSpec.describe "stubbing FOO" do it "can stub FOO with a different value" do stub_const("FOO", 5) expect(FOO).to eq(5) @@ -33,7 +32,7 @@ Feature: Stub Defined Constant end module MyGem - describe SomeClass do + RSpec.describe SomeClass do it "stubs the nested constant when it is fully qualified" do stub_const("MyGem::SomeClass::FOO", 5) expect(SomeClass::FOO).to eq(5) @@ -54,7 +53,7 @@ Feature: Stub Defined Constant end module MyGem - describe SomeClass do + RSpec.describe SomeClass do let(:fake_class) { Class.new } it "does not transfer nested constants by default" do diff --git a/features/mutating_constants/stub_undefined_constant.feature b/features/mutating_constants/stub_undefined_constant.feature index bd5d13f40..002f735da 100644 --- a/features/mutating_constants/stub_undefined_constant.feature +++ b/features/mutating_constants/stub_undefined_constant.feature @@ -1,14 +1,14 @@ Feature: Stub Undefined Constant - Use `stub_const` to stub constants. When the constant is not already defined, - all the necessary intermediary modules will be dynamically created. When the - example completes, the intermediary module constants will be removed to return - the constant state to how it started. + Use `stub_const` to stub constants. When the constant is not already defined, all the + necessary intermediary modules will be dynamically created. When the example completes, + the intermediary module constants will be removed to return the constant state to how it + started. Scenario: Stub top-level constant Given a file named "stub_const_spec.rb" with: """ruby - describe "stubbing FOO" do + RSpec.describe "stubbing FOO" do it "can stub undefined constant FOO" do stub_const("FOO", 5) expect(FOO).to eq(5) @@ -31,7 +31,7 @@ Feature: Stub Undefined Constant end module MyGem - describe SomeClass do + RSpec.describe SomeClass do it "can stub an arbitrarily deep constant that is undefined" do expect(defined?(SomeClass::A)).to be_falsey stub_const("MyGem::SomeClass::A::B::C", 3) diff --git a/features/old_syntax/README.md b/features/old_syntax/README.md new file mode 100644 index 000000000..a2ead9e4f --- /dev/null +++ b/features/old_syntax/README.md @@ -0,0 +1,35 @@ +Historically, rspec-mocks has used a monkey-patched syntax to allow you to mock or stub any object: + +```ruby +obj.stub(:foo).and_return(15) +obj.should_receive(:bar) +``` + +Unfortunately, this is prone to weird, confusing failures when applied to [delegate/proxy +objects](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax#delegation_issues). For a method like `stub` to work properly, it must be defined on every object in the +system, but RSpec does not own every object in the system and cannot ensure that it always +works consistently. + +For this reason, in RSpec 2.14, we introduced a new syntax that avoids monkey patching +altogether. It's the syntax shown in all examples of this documentation outside of this +directory. As of RSpec 3, we consider this to be the main, recommended syntax of rspec- +mocks. The old monkey-patched syntax continues to work, but you will get a deprecation +warning if you use it without explicitly opting-in to it: + +```ruby +# If you're using rspec-core: +RSpec.configure do |config| + config.mock_with :rspec do |mocks| + mocks.syntax = :should + end +end + +# Or, if you're using rspec-mocks in another context: +RSpec::Mocks.configuration.syntax = :should +``` + +We have no plans to ever kill the old syntax, but we may extract it into an external gem in +RSpec 4. + +If you have an old project that uses the old syntax and you want to update it to the current +syntax, checkout [transpec](http://yujinakayama.me/transpec/). diff --git a/features/old_syntax/any_instance.feature b/features/old_syntax/any_instance.feature new file mode 100644 index 000000000..d561303a9 --- /dev/null +++ b/features/old_syntax/any_instance.feature @@ -0,0 +1,105 @@ +@allow-old-syntax +Feature: `any_instance` + + `any_instance` is the old way to stub or mock any instance of a class but carries the baggage of a global monkey patch on all classes. + Note that we [generally recommend against](../working-with-legacy-code/any-instance) using this feature. + + Background: + Given a file named "spec/spec_helper.rb" with: + """ruby + RSpec.configure do |config| + config.mock_with :rspec do |mocks| + mocks.syntax = :should + end + end + """ + And a file named ".rspec" with: + """ + --require spec_helper + """ + + Scenario: Stub a method on any instance of a class + Given a file named "spec/example_spec.rb" with: + """ruby + RSpec.describe "Stubbing a method with any_instance" do + it "returns the specified value on any instance of the class" do + Object.any_instance.stub(:foo).and_return(:return_value) + + o = Object.new + expect(o.foo).to eq(:return_value) + end + end + """ + When I run `rspec spec/example_spec.rb` + Then the examples should all pass + + Scenario: Stub multiple methods on any instance of a class + Given a file named "spec/example_spec.rb" with: + """ruby + RSpec.describe "Stubbing multiple methods with any_instance" do + it "returns the specified values for the givne messages" do + Object.any_instance.stub(:foo => 'foo', :bar => 'bar') + + o = Object.new + expect(o.foo).to eq('foo') + expect(o.bar).to eq('bar') + end + end + """ + When I run `rspec spec/example_spec.rb` + Then the examples should all pass + + Scenario: Stubbing any instance of a class with specific arguments + Given a file named "spec/example_spec.rb" with: + """ruby + RSpec.describe "Stubbing any instance with arguments" do + it "returns the stubbed value when arguments match" do + Object.any_instance.stub(:foo).with(:param_one, :param_two).and_return(:result_one) + Object.any_instance.stub(:foo).with(:param_three, :param_four).and_return(:result_two) + + o = Object.new + expect(o.foo(:param_one, :param_two)).to eq(:result_one) + expect(o.foo(:param_three, :param_four)).to eq(:result_two) + end + end + """ + When I run `rspec spec/example_spec.rb` + Then the examples should all pass + + Scenario: Block implementation is passed the receiver as first arg + Given a file named "spec/example_spec.rb" with: + """ruby + RSpec.describe "Stubbing any instance of a class" do + it 'yields the receiver to the block implementation' do + Array.any_instance.stub(:shuffle) do |array| + array.unshift(array.pop) + end + + expect([1, 2, 3].shuffle).to eq([3, 1, 2]) + end + end + """ + When I run `rspec spec/example_spec.rb` + Then the examples should all pass + + Scenario: Expect a message on any instance of a class + Given a file named "spec/example_spec.rb" with: + """ruby + RSpec.describe "Expecting a message on any instance of a class" do + before do + Object.any_instance.should_receive(:foo) + end + + it "passes when an instance receives the message" do + Object.new.foo + end + + it "fails when no instance receives the message" do + Object.new.to_s + end + end + """ + When I run `rspec spec/example_spec.rb` + Then it should fail with the following output: + | 2 examples, 1 failure | + | Exactly one instance should have received the following message(s) but didn't: foo | diff --git a/features/old_syntax/should_receive.feature b/features/old_syntax/should_receive.feature new file mode 100644 index 000000000..1ded4d97b --- /dev/null +++ b/features/old_syntax/should_receive.feature @@ -0,0 +1,90 @@ +@allow-old-syntax +Feature: `should_receive` + + `should_receive` is the old way to [expect messages](../basics/expecting-messages) but carries the + baggage of a global monkey patch on all objects. It supports the + same fluent interface for [setting constraints](../setting-constraints) and [configuring responses](../configuring-responses). + + Similarly, you can use `should_not_receive` to set a negative message expectation. + + Background: + Given a file named "spec/spec_helper.rb" with: + """ruby + RSpec.configure do |config| + config.mock_with :rspec do |mocks| + mocks.syntax = :should + end + end + """ + And a file named ".rspec" with: + """ + --require spec_helper + """ + + Scenario: Failing positive message expectation + Given a file named "spec/unfulfilled_message_expectation_spec.rb" with: + """ruby + RSpec.describe "An unfulfilled message expectation" do + it "triggers a failure" do + dbl = double("Some Collaborator") + dbl.should_receive(:foo) + end + end + """ + When I run `rspec spec/unfulfilled_message_expectation_spec.rb` + Then it should fail with: + """ + 1) An unfulfilled message expectation triggers a failure + Failure/Error: dbl.should_receive(:foo) + (Double "Some Collaborator").foo(any args) + expected: 1 time with any arguments + received: 0 times with any arguments + """ + + Scenario: Passing positive message expectation + Given a file named "spec/fulfilled_message_expectation_spec.rb" with: + """ruby + RSpec.describe "A fulfilled message expectation" do + it "passes" do + dbl = double("Some Collaborator") + dbl.should_receive(:foo) + dbl.foo + end + end + """ + When I run `rspec spec/fulfilled_message_expectation_spec.rb` + Then the examples should all pass + + Scenario: Failing negative message expectation + Given a file named "spec/negative_message_expectation_spec.rb" with: + """ruby + RSpec.describe "A negative message expectation" do + it "fails when the message is received" do + dbl = double("Some Collaborator").as_null_object + dbl.should_not_receive(:foo) + dbl.foo + end + end + """ + When I run `rspec spec/negative_message_expectation_spec.rb` + Then it should fail with: + """ + 1) A negative message expectation fails when the message is received + Failure/Error: dbl.foo + (Double "Some Collaborator").foo(no args) + expected: 0 times with any arguments + received: 1 time + """ + + Scenario: Passing negative message expectation + Given a file named "spec/negative_message_expectation_spec.rb" with: + """ruby + RSpec.describe "A negative message expectation" do + it "passes if the message is never received" do + dbl = double("Some Collaborator").as_null_object + dbl.should_not_receive(:foo) + end + end + """ + When I run `rspec spec/negative_message_expectation_spec.rb` + Then the examples should all pass diff --git a/features/old_syntax/stub.feature b/features/old_syntax/stub.feature new file mode 100644 index 000000000..e08ea5e94 --- /dev/null +++ b/features/old_syntax/stub.feature @@ -0,0 +1,51 @@ +@allow-old-syntax +Feature: `stub` + + `stub` is the old way to [allow messages](../basics/allowing-messages) but carries the baggage of a + global monkey patch on all objects. It supports the same fluent + interface for [setting constraints](../setting-constraints) and [configuring responses](../configuring-responses). You can also pass `stub` a hash + of message/return-value pairs, which acts like `allow(obj).to receive_messages(hash)`, + but does not support further customization through the fluent interface. + + Background: + Given a file named "spec/spec_helper.rb" with: + """ruby + RSpec.configure do |config| + config.mock_with :rspec do |mocks| + mocks.syntax = :should + end + end + """ + And a file named ".rspec" with: + """ + --require spec_helper + """ + + Scenario: Stub a method + Given a file named "spec/stub_spec.rb" with: + """ruby + RSpec.describe "Stubbing a method" do + it "configures how the object responds" do + dbl = double + dbl.stub(:foo).and_return(13) + expect(dbl.foo).to eq(13) + end + end + """ + When I run `rspec spec/stub_spec.rb` + Then the examples should all pass + + Scenario: Stub multiple methods by passing a hash + Given a file named "spec/stub_multiple_methods_spec.rb" with: + """ruby + RSpec.describe "Stubbing multiple methods" do + it "stubs each named method with the given return value" do + dbl = double + dbl.stub(:foo => 13, :bar => 10) + expect(dbl.foo).to eq(13) + expect(dbl.bar).to eq(10) + end + end + """ + When I run `rspec spec/stub_multiple_methods_spec.rb` + Then the examples should all pass diff --git a/features/old_syntax/stub_chain.feature b/features/old_syntax/stub_chain.feature new file mode 100644 index 000000000..036d52f74 --- /dev/null +++ b/features/old_syntax/stub_chain.feature @@ -0,0 +1,69 @@ +@allow-old-syntax +Feature: `stub_chain` + + `stub_chain` is the old way to [allow a message chain](../working-with-legacy-code/message-chains) but carries the + baggage of a global monkey patch on all objects. As with + `receive_message_chain`, use with care; we recommend treating usage of `stub_chain` as a + code smell. + + Background: + Given a file named "spec/spec_helper.rb" with: + """ruby + RSpec.configure do |config| + config.mock_with :rspec do |mocks| + mocks.syntax = :should + end + end + """ + And a file named ".rspec" with: + """ + --require spec_helper + """ + + Scenario: Use `stub_chain` on a double + Given a file named "spec/stub_chain_spec.rb" with: + """ruby + RSpec.describe "Using stub_chain on a double" do + let(:dbl) { double } + + example "using a string and a block" do + dbl.stub_chain("foo.bar") { :baz } + expect(dbl.foo.bar).to eq(:baz) + end + + example "using symbols and a hash" do + dbl.stub_chain(:foo, :bar => :baz) + expect(dbl.foo.bar).to eq(:baz) + end + + example "using symbols and a block" do + dbl.stub_chain(:foo, :bar) { :baz } + expect(dbl.foo.bar).to eq(:baz) + end + end + """ + When I run `rspec spec/stub_chain_spec.rb` + Then the examples should all pass + + Scenario: Use `stub_chain` on any instance of a class + Given a file named "spec/stub_chain_spec.rb" with: + """ruby + RSpec.describe "Using any_instance.stub_chain" do + example "using a string and a block" do + Object.any_instance.stub_chain("foo.bar") { :baz } + expect(Object.new.foo.bar).to eq(:baz) + end + + example "using symbols and a hash" do + Object.any_instance.stub_chain(:foo, :bar => :baz) + expect(Object.new.foo.bar).to eq(:baz) + end + + example "using symbols and a block" do + Object.any_instance.stub_chain(:foo, :bar) { :baz } + expect(Object.new.foo.bar).to eq(:baz) + end + end + """ + When I run `rspec spec/stub_chain_spec.rb` + Then the examples should all pass diff --git a/features/old_syntax/unstub.feature b/features/old_syntax/unstub.feature new file mode 100644 index 000000000..fd6a45d0b --- /dev/null +++ b/features/old_syntax/unstub.feature @@ -0,0 +1,43 @@ +@allow-old-syntax +Feature: `unstub` + + `unstub` removes a method stub, essentially cleaning up the method + stub early, rather than waiting for the cleanup that runs at the end + of the example. The newer non-monkey-patching syntax does not have a direct + equivalent but in most situations you can achieve the same behavior using + [`and_call_original`](../configuring-responses/calling-the-original-implementation). The difference is that `obj.unstub(:foo)` completely cleans up the `foo` + method stub, whereas `allow(obj).to receive(:foo).and_call_original` continues to + observe calls to the method (important when you are using [spies](../basics/spies)), which could affect the + method's behavior if it does anything with `caller` as it will include additional rspec stack + frames. + + Background: + Given a file named "spec/spec_helper.rb" with: + """ruby + RSpec.configure do |config| + config.mock_with :rspec do |mocks| + mocks.syntax = :should + end + end + """ + And a file named ".rspec" with: + """ + --require spec_helper + """ + + Scenario: Unstub a method + Given a file named "spec/unstub_spec.rb" with: + """ruby + RSpec.describe "Unstubbing a method" do + it "restores the original behavior" do + string = "hello world" + string.stub(:reverse) { "hello dlrow" } + + expect { + string.unstub(:reverse) + }.to change { string.reverse }.from("hello dlrow").to("dlrow olleh") + end + end + """ + When I run `rspec spec/unstub_spec.rb` + Then the examples should all pass diff --git a/features/outside_rspec/minitest.feature b/features/outside_rspec/minitest.feature new file mode 100644 index 000000000..5520db60c --- /dev/null +++ b/features/outside_rspec/minitest.feature @@ -0,0 +1,80 @@ +Feature: Integrate with Minitest + + rspec-mocks is a stand-alone gem that can be integrated with any test framework. The + example below demonstrates using rspec-mocks with [minitest](http://docs.seattlerb.org/minitest/), but these steps + would apply when integrating rspec-mocks with any library or framework: + + * Include `RSpec::Mocks::ExampleMethods` in your test context. This provides rspec-mocks' API. + * Call `RSpec::Mocks.setup` before a test begins. + * Call `RSpec::Mocks.verify` after a test completes to verify message expectations. Note + that this step is optional; rspec-core, for example, skips this when an example has already failed. + * Call `RSpec::Mocks.teardown` after a test completes (and after `verify`) to cleanup. This + _must_ be called, even if an error has occurred, so it generally goes in an `ensure` clause. + + Scenario: Use rspec-mocks with Minitest + Given a file named "test/test_helper.rb" with: + """ruby + require 'minitest/autorun' + require 'rspec/mocks' + + module MinitestRSpecMocksIntegration + include ::RSpec::Mocks::ExampleMethods + + def before_setup + ::RSpec::Mocks.setup + super + end + + def after_teardown + super + ::RSpec::Mocks.verify + ensure + ::RSpec::Mocks.teardown + end + end + + Minitest::Test.send(:include, MinitestRSpecMocksIntegration) + """ + And a file named "test/rspec_mocks_test.rb" with: + """ruby + require 'test_helper' + + class RSpecMocksTest < Minitest::Test + def test_passing_positive_expectation + dbl = double + expect(dbl).to receive(:message) + dbl.message + end + + def test_failing_positive_expectation + dbl = double + expect(dbl).to receive(:message) + end + + def test_passing_negative_expectation + dbl = double + expect(dbl).to_not receive(:message) + end + + def test_failing_negative_expectation + dbl = double + expect(dbl).to_not receive(:message) + dbl.message + end + end + """ + When I run `ruby -Itest test/rspec_mocks_test.rb` + Then it should fail with the following output: + | 1) Error: | + | RSpecMocksTest#test_failing_negative_expectation: | + | RSpec::Mocks::MockExpectationError: (Double).message(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 2) Error: | + | RSpecMocksTest#test_failing_positive_expectation: | + | RSpec::Mocks::MockExpectationError: (Double).message(any args) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 4 runs, 0 assertions, 0 failures, 2 errors, 0 skips | diff --git a/features/outside_rspec/standalone.feature b/features/outside_rspec/standalone.feature index 44392ad00..679c9e3d3 100644 --- a/features/outside_rspec/standalone.feature +++ b/features/outside_rspec/standalone.feature @@ -1,10 +1,9 @@ -Feature: standalone +Feature: Standalone - require "rspec/mocks/standalone" to expose the mock framework - outside the RSpec environment. This is especially useful for - exploring rspec-mocks in irb. + `require "rspec/mocks/standalone"` to expose the API at the top level (e.g. `main`) outside + the RSpec environment. This is especially useful for exploring rspec-mocks in irb. - Scenario: method stub outside rspec + Scenario: Allow a message outside RSpec Given a file named "example.rb" with: """ruby require "rspec/mocks/standalone" @@ -16,7 +15,7 @@ Feature: standalone When I run `ruby example.rb` Then the output should contain "Hello!" - Scenario: message expectation outside rspec + Scenario: Expect a message outside RSpec Given a file named "example.rb" with: """ruby require "rspec/mocks/standalone" @@ -27,7 +26,8 @@ Feature: standalone RSpec::Mocks.verify """ When I run `ruby example.rb` - Then the output should contain "RSpec::Mocks::MockExpectationError" - And the output should contain "say_hi(any args)" - And the output should contain "expected: 1 time" - And the output should contain "received: 0 times" + Then it should fail with the following output: + | (Double "greeter").say_hi(any args) | + | RSpec::Mocks::MockExpectationError | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | diff --git a/features/setting_constraints/README.md b/features/setting_constraints/README.md new file mode 100644 index 000000000..324da7587 --- /dev/null +++ b/features/setting_constraints/README.md @@ -0,0 +1,6 @@ +RSpec provides a fluent interface off of `expect(...).to receive(...)` that allows you to +further constrain what you expect: the arguments, the number of times, and the ordering of +multiple messages. + +Although not shown here, this fluent interface is also supported by [spies](../basics/spies), off of +`have_received(...)`. diff --git a/features/setting_constraints/matching_arguments.feature b/features/setting_constraints/matching_arguments.feature new file mode 100644 index 000000000..269bc029e --- /dev/null +++ b/features/setting_constraints/matching_arguments.feature @@ -0,0 +1,97 @@ +Feature: Matching arguments + + Use `with` to specify the expected arguments. A [message expectation](../basics/expecting-messages) constrained by `with` + will only be satisfied when called with matching arguments. A canned response for an + [allowed message](../basics/allowing-messages) will only be used when the arguments match. + + | To match... | ...use an expression like: | ...which matches calls like: | + | --------------------------------------------------- | ------------------------------- | ------------------------------------- | + | Literal arguments | `with(1, true)` | `foo(1, true)` | + | Anything that supports case equality (`===`) | `with(/bar/)` | `foo("barn")` | + | Any list of args | `with(any_args)` | `foo()`
`foo(1)`
`foo(:bar, 2)` | + | An empty list of args | `with(no_args)` | `foo()` | + | Anything for a given positional arg | `with(3, anything)` | `foo(3, nil)`
`foo(3, :bar)` | + | Against an interface | `with(duck_type(:each))` | `foo([])` | + | A boolean | `with(3, boolean)` | `foo(3, true)`
`foo(3, false)` | + | A subset of a hash | `with(hash_including(:a => 1))` | `foo(:a => 1, :b => 2)` | + | An excluded subset of a hash | `with(hash_excluding(:a => 1))` | `foo(:b => 2)` | + | A subset of an array | `with(array_including(:a, :b))` | `foo([:a, :b, :c])` | + | An instance of a specific class | `with(instance_of(Fixnum))` | `foo(3)` | + | An object with a given module in its ancestors list | `with(kind_of(Numeric))` | `foo(3)` | + | Any RSpec matcher | `with()` | `foo()` | + + Scenario: Basic example + Given a file named "basic_example_spec.rb" with: + """ruby + RSpec.describe "Constraining a message expectation using with" do + let(:dbl) { double } + before { expect(dbl).to receive(:foo).with(1, anything, /bar/) } + + it "passes when the args match" do + dbl.foo(1, nil, "barn") + end + + it "fails when the args do not match" do + dbl.foo(1, nil, "other") + end + end + """ + When I run `rspec basic_example_spec.rb` + Then it should fail with the following output: + | 2 examples, 1 failure | + | | + | Failure/Error: dbl.foo(1, nil, "other") | + | Double received :foo with unexpected arguments | + | expected: (1, anything, /bar/) | + | got: (1, nil, "other") | + + Scenario: Using a custom matcher + Given a file named "custom_matcher_spec.rb" with: + """ruby + RSpec::Matchers.define :a_multiple_of do |x| + match { |actual| (actual % x).zero? } + end + + RSpec.describe "Using a custom matcher" do + let(:dbl) { double } + before { expect(dbl).to receive(:foo).with(a_multiple_of(3)) } + + it "passes when the args match" do + dbl.foo(12) + end + + it "fails when the args do not match" do + dbl.foo(13) + end + end + """ + When I run `rspec custom_matcher_spec.rb` + Then it should fail with the following output: + | 2 examples, 1 failure | + | | + | Failure/Error: dbl.foo(13) | + | Double received :foo with unexpected arguments | + | expected: (a multiple of 3) | + | got: (13) | + + Scenario: Responding differently based on the arguments + Given a file named "responding_differently_spec.rb" with: + """ruby + RSpec.describe "Using #with to constrain responses" do + specify "its response depends on the arguments" do + dbl = double + + # Set a default for any unmatched args + allow(dbl).to receive(:foo).and_return(:default) + + allow(dbl).to receive(:foo).with(1).and_return(1) + allow(dbl).to receive(:foo).with(2).and_return(2) + + expect(dbl.foo(0)).to eq(:default) + expect(dbl.foo(1)).to eq(1) + expect(dbl.foo(2)).to eq(2) + end + end + """ + When I run `rspec responding_differently_spec.rb` + Then the examples should all pass diff --git a/features/setting_constraints/message_order.feature b/features/setting_constraints/message_order.feature new file mode 100644 index 000000000..11dc22bae --- /dev/null +++ b/features/setting_constraints/message_order.feature @@ -0,0 +1,63 @@ +Feature: Message Order + + You can use `ordered` to constrain the order of multiple message expectations. This is not + generally recommended because in most situations the order doesn't matter and using + `ordered` would make your spec brittle, but it's occasionally useful. When you use `ordered`, + the example will only pass if the messages are received in the declared order. + + Scenario: Passing example + Given a file named "passing_example_spec.rb" with: + """ruby + RSpec.describe "Constraining order" do + it "passes when the messages are received in declared order" do + collaborator_1 = double("Collaborator 1") + collaborator_2 = double("Collaborator 2") + + expect(collaborator_1).to receive(:step_1).ordered + expect(collaborator_2).to receive(:step_2).ordered + expect(collaborator_1).to receive(:step_3).ordered + + collaborator_1.step_1 + collaborator_2.step_2 + collaborator_1.step_3 + end + end + """ + When I run `rspec passing_example_spec.rb` + Then the examples should all pass + + Scenario: Failing examples + Given a file named "failing_examples_spec.rb" with: + """ruby + RSpec.describe "Constraining order" do + it "fails when messages are received out of order on one collaborator" do + collaborator_1 = double("Collaborator 1") + + expect(collaborator_1).to receive(:step_1).ordered + expect(collaborator_1).to receive(:step_2).ordered + + collaborator_1.step_2 + collaborator_1.step_1 + end + + it "fails when messages are received out of order between collaborators" do + collaborator_1 = double("Collaborator 1") + collaborator_2 = double("Collaborator 2") + + expect(collaborator_1).to receive(:step_1).ordered + expect(collaborator_2).to receive(:step_2).ordered + + collaborator_2.step_2 + collaborator_1.step_1 + end + end + """ + When I run `rspec failing_examples_spec.rb --order defined` + Then the examples should all fail, producing the following output: + | 1) Constraining order fails when messages are received out of order on one collaborator | + | Failure/Error: collaborator_1.step_2 | + | Double "Collaborator 1" received :step_2 out of order | + | | + | 2) Constraining order fails when messages are received out of order between collaborators | + | Failure/Error: collaborator_2.step_2 | + | Double "Collaborator 2" received :step_2 out of order | diff --git a/features/setting_constraints/receive_counts.feature b/features/setting_constraints/receive_counts.feature new file mode 100644 index 000000000..de9c7c828 --- /dev/null +++ b/features/setting_constraints/receive_counts.feature @@ -0,0 +1,189 @@ +Feature: Receive Counts + + When [expecting a message](../basics/expecting-messages), you can specify how many times you expect the message to be + received: + + * `expect(...).to receive(...).once` + * `expect(...).to receive(...).twice` + * `expect(...).to receive(...).exactly(n).times` + * `expect(...).to receive(...).at_least(:once)` + * `expect(...).to receive(...).at_least(:twice)` + * `expect(...).to receive(...).at_least(n).times` + * `expect(...).to receive(...).at_most(:once)` + * `expect(...).to receive(...).at_most(:twice)` + * `expect(...).to receive(...).at_most(n).times` + + If you don't specify an expected receive count, it defaults to `once`. + + Background: + Given a file named "lib/account.rb" with: + """ruby + class Account + def initialize(logger) + @logger = logger + end + + def open + @logger.account_opened + end + end + """ + + Scenario: Passing examples + Given a file named "spec/account_spec.rb" with: + """ruby + require 'account' + + RSpec.describe Account do + let(:logger) { double("Logger") } + let(:account) { Account.new(logger) } + + example "once" do + expect(logger).to receive(:account_opened).once + account.open + end + + example "twice" do + expect(logger).to receive(:account_opened).twice + account.open + account.open + end + + example "exactly(n).times" do + expect(logger).to receive(:account_opened).exactly(3).times + account.open + account.open + account.open + end + + example "at_least(:once)" do + expect(logger).to receive(:account_opened).at_least(:once) + account.open + account.open + end + + example "at_least(:twice)" do + expect(logger).to receive(:account_opened).at_least(:twice) + account.open + account.open + account.open + end + + example "at_least(n).times" do + expect(logger).to receive(:account_opened).at_least(3).times + account.open + account.open + account.open + account.open + end + + example "at_most(:once)" do + expect(logger).to receive(:account_opened).at_most(:once) + end + + example "at_most(:twice)" do + expect(logger).to receive(:account_opened).at_most(:twice) + account.open + end + + example "at_most(n).times" do + expect(logger).to receive(:account_opened).at_most(3).times + account.open + account.open + end + end + """ + When I run `rspec spec/account_spec.rb` + Then the examples should all pass + + Scenario: Failing examples + Given a file named "spec/account_spec.rb" with: + """ruby + require 'account' + + RSpec.describe Account do + let(:logger) { double("Logger") } + let(:account) { Account.new(logger) } + + example "once" do + expect(logger).to receive(:account_opened).once + account.open + account.open + end + + example "twice" do + expect(logger).to receive(:account_opened).twice + account.open + end + + example "exactly(n).times" do + expect(logger).to receive(:account_opened).exactly(3).times + account.open + account.open + end + + example "at_least(:once)" do + expect(logger).to receive(:account_opened).at_least(:once) + end + + example "at_least(:twice)" do + expect(logger).to receive(:account_opened).at_least(:twice) + account.open + end + + example "at_least(n).times" do + expect(logger).to receive(:account_opened).at_least(3).times + account.open + account.open + end + + example "at_most(:once)" do + expect(logger).to receive(:account_opened).at_most(:once) + account.open + account.open + end + + example "at_most(:twice)" do + expect(logger).to receive(:account_opened).at_most(:twice) + account.open + account.open + account.open + end + + example "at_most(n).times" do + expect(logger).to receive(:account_opened).at_most(3).times + account.open + account.open + account.open + account.open + end + end + """ + When I run `rspec spec/account_spec.rb --order defined` + Then the examples should all fail, producing the following output: + | expected: 1 time with any arguments | + | received: 2 times | + | | + | expected: 2 times with any arguments | + | received: 1 time with any arguments | + | | + | expected: 3 times with any arguments | + | received: 2 times with any arguments | + | | + | expected: at least 1 time with any arguments | + | received: 0 times with any arguments | + | | + | expected: at least 2 times with any arguments | + | received: 1 time with any arguments | + | | + | expected: at least 3 times with any arguments | + | received: 2 times with any arguments | + | | + | expected: at most 1 time with any arguments | + | received: 2 times | + | | + | expected: at most 2 times with any arguments | + | received: 3 times | + | | + | expected: at most 3 times with any arguments | + | received: 4 times | diff --git a/features/spies/spy_partial_mock_method.feature b/features/spies/spy_partial_mock_method.feature deleted file mode 100644 index 63d7ec974..000000000 --- a/features/spies/spy_partial_mock_method.feature +++ /dev/null @@ -1,34 +0,0 @@ -Feature: Spy on a stubbed method on a partial mock - - You can also use `have_received` to verify that a stubbed method was invoked - on a partial mock. - - Scenario: verify a stubbed method - Given a file named "verified_spy_spec.rb" with: - """ruby - describe "have_received" do - it "passes when the expectation is met" do - invitation = Object.new - invitation.stub(:deliver => true) - invitation.deliver - expect(invitation).to have_received(:deliver) - end - end - """ - When I run `rspec verified_spy_spec.rb` - Then the examples should all pass - - Scenario: fail to verify a stubbed method - Given a file named "failed_spy_spec.rb" with: - """ruby - describe "have_received" do - it "fails when the expectation is not met" do - invitation = Object.new - invitation.stub(:deliver => true) - expect(invitation).to have_received(:deliver) - end - end - """ - When I run `rspec failed_spy_spec.rb` - Then the output should contain "expected: 1 time" - And the output should contain "received: 0 times" diff --git a/features/spies/spy_pure_mock_method.feature b/features/spies/spy_pure_mock_method.feature deleted file mode 100644 index 6b1f3209f..000000000 --- a/features/spies/spy_pure_mock_method.feature +++ /dev/null @@ -1,76 +0,0 @@ -Feature: Spy on a stubbed method on a pure mock - - You can use `have_received` to verify that a stubbed method was invoked, - rather than setting an expectation for it to be invoked beforehand. - - Scenario: verify a stubbed method - Given a file named "verified_spy_spec.rb" with: - """ruby - describe "have_received" do - it "passes when the expectation is met" do - invitation = double('invitation', :deliver => true) - invitation.deliver - expect(invitation).to have_received(:deliver) - end - end - """ - When I run `rspec verified_spy_spec.rb` - Then the examples should all pass - - Scenario: verify a stubbed method with message expectations - Given a file named "verified_message_expectations_spec.rb" with: - """ruby - describe "have_received" do - it "passes when the expectation is met" do - invitation = double('invitation', :deliver => true) - 2.times { invitation.deliver(:expected, :arguments) } - expect(invitation).to have_received(:deliver). - with(:expected, :arguments). - twice - end - end - """ - When I run `rspec verified_message_expectations_spec.rb` - Then the examples should all pass - - Scenario: fail to verify a stubbed method - Given a file named "failed_spy_spec.rb" with: - """ruby - describe "have_received" do - it "fails when the expectation is not met" do - invitation = double('invitation', :deliver => true) - expect(invitation).to have_received(:deliver) - end - end - """ - When I run `rspec failed_spy_spec.rb` - Then the output should contain "expected: 1 time" - And the output should contain "received: 0 times" - - Scenario: fail to verify message expectations - Given a file named "failed_message_expectations_spec.rb" with: - """ruby - describe "have_received" do - it "fails when the arguments are different" do - invitation = double('invitation', :deliver => true) - invitation.deliver(:unexpected) - expect(invitation).to have_received(:deliver).with(:expected, :arguments) - end - end - """ - When I run `rspec failed_message_expectations_spec.rb` - Then the output should contain "expected: (:expected, :arguments)" - And the output should contain "got: (:unexpected)" - - Scenario: generate a spy message - Given a file named "spy_message_spec.rb" with: - """ruby - describe "have_received" do - subject(:invitation) { double('invitation', :deliver => true) } - before { invitation.deliver } - - it { should have_received(:deliver) } - end - """ - When I run `rspec --format documentation spy_message_spec.rb` - Then the output should contain "should have received deliver" diff --git a/features/spies/spy_unstubbed_method.feature b/features/spies/spy_unstubbed_method.feature deleted file mode 100644 index 8a82c5a6c..000000000 --- a/features/spies/spy_unstubbed_method.feature +++ /dev/null @@ -1,18 +0,0 @@ -Feature: Spy on an unstubbed method - - Using have_received on an unstubbed method will never pass, so rspec-mocks - issues a helpful error message. - - Scenario: fail to verify a stubbed method - Given a file named "failed_spy_spec.rb" with: - """ruby - describe "have_received" do - it "raises a helpful error for unstubbed methods" do - object = Object.new - object.object_id - expect(object).to have_received(:object_id) - end - end - """ - When I run `rspec failed_spy_spec.rb` - Then the output should contain "that method has not been stubbed" diff --git a/features/step_definitions/additional_cli_steps.rb b/features/step_definitions/additional_cli_steps.rb index 22cd0296a..d9f3f7093 100644 --- a/features/step_definitions/additional_cli_steps.rb +++ b/features/step_definitions/additional_cli_steps.rb @@ -3,9 +3,19 @@ step %q{the exit status should be 0} end -# Useful for when the output is slightly different on different versions of ruby -Then /^the output should contain "([^"]*)" or "([^"]*)"$/ do |string1, string2| - unless [string1, string2].any? { |s| all_output =~ regexp(s) } - fail %Q{Neither "#{string1}" or "#{string2}" were found in:\n#{all_output}} - end +Then /^the examples should all fail, producing the following output:$/ do |table| + step %q{the exit status should be 1} + examples, failures = all_output.match(/(\d+) examples?, (\d+) failures?/).captures.map(&:to_i) + + expect(examples).to be > 0 + expect(examples).to eq(failures) + + lines = table.raw.flatten.reject(&:empty?) + expect(all_output).to include(*lines) +end + +Then /^it should fail with the following output:$/ do |table| + step %q{the exit status should be 1} + lines = table.raw.flatten.reject(&:empty?) + expect(all_output).to include(*lines) end diff --git a/features/support/disallow_certain_apis.rb b/features/support/disallow_certain_apis.rb new file mode 100644 index 000000000..08b93b4bf --- /dev/null +++ b/features/support/disallow_certain_apis.rb @@ -0,0 +1,24 @@ +# This file is designed to prevent the use of certain APIs that +# we don't want used from our cukes, since they function as documentation. + +if defined?(Cucumber) + require 'shellwords' + Before('~@allow-old-syntax') do + set_env('SPEC_OPTS', "-r#{Shellwords.escape(__FILE__)}") + end +else + module DisallowOneLinerShould + def should(*) + raise "one-liner should is not allowed" + end + + def should_not(*) + raise "one-liner should_not is not allowed" + end + end + + RSpec.configure do |rspec| + rspec.disable_monkey_patching! + rspec.include DisallowOneLinerShould + end +end diff --git a/features/test_frameworks/test_unit.feature b/features/test_frameworks/test_unit.feature deleted file mode 100644 index d60306993..000000000 --- a/features/test_frameworks/test_unit.feature +++ /dev/null @@ -1,54 +0,0 @@ -Feature: Test::Unit integration - - rspec-mocks is a stand-alone gem that can be used without the rest of - RSpec. If you like the way Test::Unit (or MiniTest) organizes tests, but - prefer RSpec's approach to mocking/stubbing/doubles etc, you can have both. - - The one downside is that failures are reported as errors with MiniTest. - - Scenario: use rspec/mocks with Test::Unit - Given a file named "rspec_mocks_test.rb" with: - """ruby - require 'test/unit' - require 'rspec/mocks' - - class RSpecMocksTest < Test::Unit::TestCase - include RSpec::Mocks::ExampleMethods - - def setup - RSpec::Mocks.setup - end - - def teardown - RSpec::Mocks.verify - ensure - RSpec::Mocks.teardown - end - - def test_passing_positive_expectation - obj = Object.new - expect(obj).to receive(:message) - obj.message - end - - def test_failing_positive_expectation - obj = Object.new - expect(obj).to receive(:message) - obj.message - end - - def test_passing_negative_expectation - obj = Object.new - expect(obj).to_not receive(:message) - end - - def test_failing_negative_expectation - obj = Object.new - expect(obj).to_not receive(:message) - obj.message - end - end - """ - When I run `ruby rspec_mocks_test.rb` - Then the output should contain "4 tests, 0 assertions, 0 failures, 1 errors" or "4 tests, 0 assertions, 1 failures, 0 errors" - And the output should contain "expected: 0 times with any arguments" diff --git a/features/verifying_doubles/README.md b/features/verifying_doubles/README.md index 491f6ec9f..691c75fdd 100644 --- a/features/verifying_doubles/README.md +++ b/features/verifying_doubles/README.md @@ -1,17 +1,13 @@ -## Verifying Doubles +Verifying doubles are a stricter alternative to [normal doubles](./basics/test-doubles) that provide guarantees about +what is being verified. When using verifying doubles, RSpec will check that the methods +being stubbed are actually present on the underlying object if it is available. Prefer using +veryifing doubles over normal doubles. -Verifying doubles are a stricter alternative to normal doubles that provide -guarantees about what is being verified. When using verifying doubles, RSpec -will check that the methods being stubbed are actually present on the -underlying object if it is available. Prefer using veryifing doubles over -normal doubles. +No checking will happen if the underlying object or class is not defined, but when run with +it present (either as a full spec run or by explicitly preloading collaborators) a failure will be +triggered if an invalid method is being stubbed or a method is called with an invalid +number of arguments. -No checking will happen if the underlying object or class is not defined, but -when run with it present (either as a full spec run or by explicitly preloading -collaborators) a failure will be triggered if an invalid method is being -stubbed or a method is called with an invalid number of arguments. - -This dual approach allows you to move very quickly and test components in -isolation, while giving you confidence that your doubles are not a complete -fiction. Testing in isolation is optional but recommend for classes that do not -depend on third-party components. +This dual approach allows you to move very quickly and test components in isolation, while +giving you confidence that your doubles are not a complete fiction. Testing in isolation is +optional but recommend for classes that do not depend on third-party components. diff --git a/features/verifying_doubles/class_doubles.feature b/features/verifying_doubles/class_doubles.feature index 084e0bbf2..8c94cd763 100644 --- a/features/verifying_doubles/class_doubles.feature +++ b/features/verifying_doubles/class_doubles.feature @@ -1,17 +1,14 @@ Feature: Using a class double - `class_double` is provided as a complement to `instance_double`, with the - difference that it verifies _class_ methods on the given class rather than - instance methods. + `class_double` is provided as a complement to [`instance_double`](./using-an-instance-double) with the difference that it + verifies _class_ methods on the given class rather than instance methods. - In addition, it also provides a convenience method `as_stubbed_const` to - replace concrete classes with the defined double. See [mutating - constants](../mutating-constants) for more details. + In addition, it also provides a convenience method `as_stubbed_const` to replace concrete + classes with the defined double. See [mutating constants](../mutating-constants) for more details. - Note: `class_double` can be used for modules as well. We chose to stick with - the `class_double` terminology because the methods a `class_double` verifies - against are commonly called "class methods", not "module methods", even when - working with a module. + Note: `class_double` can be used for modules as well. We chose to stick with the + `class_double` terminology because the methods a `class_double` verifies against are + commonly called "class methods", not "module methods", even when working with a module. Background: Given a file named "lib/user.rb" with: @@ -39,7 +36,7 @@ Feature: Using a class double require 'user' require 'console_notifier' - describe User, '#suspend!' do + RSpec.describe User, '#suspend!' do it 'notifies the console' do notifier = class_double("ConsoleNotifier"). as_stubbed_const(:transfer_nested_constants => true) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index d6f9850dc..3f46f3bec 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -1,15 +1,13 @@ Feature: Dynamic classes - Verifying instance doubles do not support methods which the class reports to - not exist since an actual instance of the class would be required to verify - against. This is commonly the case when `method_missing` is used. - `ActiveRecord` does this to define methods from database columns. If the - object has already been loaded you may consider using an `object_double`, but - that cannot work if you are testing in isolation. - - These types of methods are supported at class level, since `respond_to?` can - be queried directly on the class. + Verifying instance doubles do not support methods which the class reports to not exist + since an actual instance of the class would be required to verify against. This is commonly + the case when `method_missing` is used. `ActiveRecord` does this to define methods from + database columns. If the object has already been loaded you may consider using an + [`object_double`](./using-an-object-double), but that cannot work if you are testing in isolation. + These types of methods are supported at class level, since `respond_to?` can be queried + directly on the class. Background: Given a file named "lib/fake_active_record.rb" with: @@ -35,7 +33,7 @@ Feature: Dynamic classes """ruby require 'user' - describe User do + RSpec.describe User do it 'can be doubled' do instance_double("User", :name => "Don") end diff --git a/features/verifying_doubles/instance_doubles.feature b/features/verifying_doubles/instance_doubles.feature index 89d704f40..e21948c92 100644 --- a/features/verifying_doubles/instance_doubles.feature +++ b/features/verifying_doubles/instance_doubles.feature @@ -1,13 +1,11 @@ Feature: Using an instance double - An `instance_double` is the most common type of verifying double. It takes a - class name or object as its first argument, then verifies that any methods - being stubbed would be present on an _instance_ of that class. If any - argument matchers are specified, it also verifies that the number of - arguments is correct. + An `instance_double` is the most common type of verifying double. It takes a class name or + object as its first argument, then verifies that any methods being stubbed would be present + on an _instance_ of that class. If any argument matchers are specified, it also verifies that the + number of arguments is correct. - For methods handled by `method_missing`, see [dynamic - classes](./dynamic-classes). + For methods handled by `method_missing`, see [dynamic classes](./dynamic-classes). Background: Given a file named "app/models/user.rb" with: @@ -50,7 +48,7 @@ Feature: Using an instance double require 'user' - describe User, '#suspend!' do + RSpec.describe User, '#suspend!' do it 'notifies the console' do notifier = instance_double("ConsoleNotifier") diff --git a/features/verifying_doubles/object_doubles.feature b/features/verifying_doubles/object_doubles.feature index 62ce7f1e8..776a28841 100644 --- a/features/verifying_doubles/object_doubles.feature +++ b/features/verifying_doubles/object_doubles.feature @@ -1,18 +1,16 @@ Feature: Using an object double - `object_double` can be used to create a double from an existing "template" - object, from which it verifies that any stubbed methods on the double also - exist on the template. This is useful for objects that are readily - constructable, but may have far-reaching side-effects such as talking to a - database or external API. In this case, using a double rather than the real - thing allows you to focus on the communication patterns of the object's - interface without having to worry about accidentally causing side-effects. - Object doubles can also be used to verify methods defined on an object using - `method_missing`, which is not possible with `instance_double`. - - In addition, `object_double` can be used with specific constant values, as - shown below. This is for niche situations, such as when dealing with - singleton objects. + `object_double` can be used to create a double from an existing "template" object, from + which it verifies that any stubbed methods on the double also exist on the template. This is + useful for objects that are readily constructable, but may have far-reaching side-effects + such as talking to a database or external API. In this case, using a double rather than the + real thing allows you to focus on the communication patterns of the object's interface + without having to worry about accidentally causing side-effects. Object doubles can also be + used to verify methods defined on an object using `method_missing`, which is not possible + with [`instance_double`](./using-an-instance-double). + + In addition, `object_double` can be used with specific constant values, as shown below. This + is for niche situations, such as when dealing with singleton objects. Scenario: doubling an existing object Given a file named "spec/user_spec.rb" with: @@ -26,7 +24,7 @@ Feature: Using an object double "saved!" if user.save end - describe '#save_user' do + RSpec.describe '#save_user' do it 'renders message on success' do user = object_double(User.new, :save => true) expect(save_user(user)).to eq("saved!") @@ -36,7 +34,6 @@ Feature: Using an object double When I run `rspec spec/user_spec.rb` Then the examples should all pass - Scenario: doubling a constant object Given a file named "spec/email_spec.rb" with: """ruby @@ -53,7 +50,7 @@ Feature: Using an object double end end - describe Email do + RSpec.describe Email do it 'logs a message when sending' do logger = object_double("MyApp::LOGGER", :info => nil).as_stubbed_const Email.send_to('hello@foo.com') diff --git a/features/verifying_doubles/partial_doubles.feature b/features/verifying_doubles/partial_doubles.feature index 687f398d6..4dadd03ea 100644 --- a/features/verifying_doubles/partial_doubles.feature +++ b/features/verifying_doubles/partial_doubles.feature @@ -1,9 +1,9 @@ Feature: Partial doubles - When the `verify_partial_doubles` configuration option is set, the same argument - and method existence checks that are performed for `object_double` are also - performed on partial doubles. You should set this unless you have a good - reason not to. It defaults to off only for backwards compatibility. + When the `verify_partial_doubles` configuration option is set, the same argument and + method existence checks that are performed for [`object_double`](./using-an-object-double) are also performed on + [partial doubles](../basics/partial-test-doubles). You should set this unless you have a good reason not to. It defaults to off + only for backwards compatibility. Scenario: doubling an existing object Given a file named "spec/user_spec.rb" with: @@ -22,7 +22,7 @@ Feature: Partial doubles end end - describe '#save_user' do + RSpec.describe '#save_user' do it 'renders message on success' do user = User.new expect(user).to receive(:saave).and_return(true) # Typo in name diff --git a/features/working_with_legacy_code/README.md b/features/working_with_legacy_code/README.md new file mode 100644 index 000000000..862f15af9 --- /dev/null +++ b/features/working_with_legacy_code/README.md @@ -0,0 +1,3 @@ +RSpec provides a few features that, while not generally recommended, can be useful when +you are getting legacy code under test (or in similar situations). Usage of these features +should be considered a code smell. diff --git a/features/working_with_legacy_code/any_instance.feature b/features/working_with_legacy_code/any_instance.feature new file mode 100644 index 000000000..f2bb5ca1f --- /dev/null +++ b/features/working_with_legacy_code/any_instance.feature @@ -0,0 +1,115 @@ +Feature: Any Instance + + rspec-mocks provides two methods, `allow_any_instance_of` and + `expect_any_instance_of`, that will allow you to stub or mock any instance of a class. They + are used in place of [`allow`](../basics/allowing-messages) or [`expect`](../basics/expecting-messages): + + ```ruby + allow_any_instance_of(Widget).to receive(:name).and_return("Wibble") + expect_any_instance_of(Widget).to receive(:name).and_return("Wobble") + ``` + + These methods add the appropriate stub or expectation to all instances of `Widget`. + + This feature is sometimes useful when working with legacy code, though in general we + discourage its use for a number of reasons: + + * The `rspec-mocks` API is designed for individual object instances, but this feature + operates on entire classes of objects. As a result there are some sematically confusing + edge cases. For example, in `expect_any_instance_of(Widget).to + receive(:name).twice` it isn't clear whether each specific instance is expected to + receive `name` twice, or if two receives total are expected. (It's the former.) + * Using this feature is often a design smell. It may be that your test is trying to do too + much or that the object under test is too complex. + * It is the most complicated feature of `rspec-mocks`, and has historically received the + most bug reports. (None of the core team actively use it, which doesn't help.) + + Scenario: Use `allow_any_instance_of` to stub a method + Given a file named "example_spec.rb" with: + """ruby + RSpec.describe "allow_any_instance_of" do + it "returns the specified value on any instance of the class" do + allow_any_instance_of(Object).to receive(:foo).and_return(:return_value) + + o = Object.new + expect(o.foo).to eq(:return_value) + end + end + """ + When I run `rspec example_spec.rb` + Then the examples should all pass + + Scenario: Use `allow_any_instance_of` to stub multiple methods + Given a file named "example_spec.rb" with: + """ruby + RSpec.describe "allow_any_instance_of" do + context "with receive_messages" do + it "stubs multiple methods" do + allow_any_instance_of(Object).to receive_messages(:foo => 'foo', :bar => 'bar') + + o = Object.new + expect(o.foo).to eq('foo') + expect(o.bar).to eq('bar') + end + end + end + """ + When I run `rspec example_spec.rb` + Then the examples should all pass + + Scenario: Stubbing any instance of a class with specific arguments + Given a file named "example_spec.rb" with: + """ruby + RSpec.describe "allow_any_instance_of" do + context "with arguments" do + it "returns the stubbed value when arguments match" do + allow_any_instance_of(Object).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) + allow_any_instance_of(Object).to receive(:foo).with(:param_three, :param_four).and_return(:result_two) + + o = Object.new + expect(o.foo(:param_one, :param_two)).to eq(:result_one) + expect(o.foo(:param_three, :param_four)).to eq(:result_two) + end + end + end + """ + When I run `rspec example_spec.rb` + Then the examples should all pass + + Scenario: Block implementation is passed the receiver as first arg + Given a file named "example_spec.rb" with: + """ruby + RSpec.describe "allow_any_instance_of" do + it 'yields the receiver to the block implementation' do + allow_any_instance_of(Array).to receive(:shuffle) do |array| + array.unshift(array.pop) + end + + expect([1, 2, 3].shuffle).to eq([3, 1, 2]) + end + end + """ + When I run `rspec example_spec.rb` + Then the examples should all pass + + Scenario: Use `expect_any_instance_of` to set a message expectation on any instance + Given a file named "example_spec.rb" with: + """ruby + RSpec.describe "expect_any_instance_of" do + before do + expect_any_instance_of(Object).to receive(:foo) + end + + it "passes when an instance receives the message" do + Object.new.foo + end + + it "fails when no instance receives the message" do + Object.new.to_s + end + end + """ + When I run `rspec example_spec.rb` + Then it should fail with the following output: + | 2 examples, 1 failure | + | Exactly one instance should have received the following message(s) but didn't: foo | diff --git a/features/working_with_legacy_code/message_chains.feature b/features/working_with_legacy_code/message_chains.feature new file mode 100644 index 000000000..1627dc990 --- /dev/null +++ b/features/working_with_legacy_code/message_chains.feature @@ -0,0 +1,79 @@ +Feature: Message Chains + + You can use `receive_message_chain` in place of `receive` to stub a chain of messages: + + ```ruby + allow(double).to receive_message_chain("foo.bar") { :baz } + allow(double).to receive_message_chain(:foo, :bar => :baz) + allow(double).to receive_message_chain(:foo, :bar) { :baz } + ```` + + Given any of these three forms: + + ```ruby + double.foo.bar # => :baz + ``` + + Common use in Rails/ActiveRecord: + + ```ruby + allow(Article).to receive_message_chain("recent.published") { [Article.new] } + ``` + + Warning: + ======== + + Chains can be arbitrarily long, which makes it quite painless to violate the Law of Demeter + in violent ways, so you should consider any use of `receive_message_chain` a code smell. + Even though not all code smells indicate real problems (think fluent interfaces), + `receive_message_chain` still results in brittle examples. For example, if you write + `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the + implementation calls `foo.baz.bar`, the stub will not work. + + Scenario: Use `receive_message_chain` on a double + Given a file named "receive_message_chain_spec.rb" with: + """ruby + RSpec.describe "Using receive_message_chain on a double" do + let(:dbl) { double } + + example "using a string and a block" do + allow(dbl).to receive_message_chain("foo.bar") { :baz } + expect(dbl.foo.bar).to eq(:baz) + end + + example "using symbols and a hash" do + allow(dbl).to receive_message_chain(:foo, :bar => :baz) + expect(dbl.foo.bar).to eq(:baz) + end + + example "using symbols and a block" do + allow(dbl).to receive_message_chain(:foo, :bar) { :baz } + expect(dbl.foo.bar).to eq(:baz) + end + end + """ + When I run `rspec receive_message_chain_spec.rb` + Then the examples should all pass + + Scenario: Use `receive_message_chain` on any instance of a class + Given a file named "receive_message_chain_spec.rb" with: + """ruby + RSpec.describe "Using receive_message_chain on any instance of a class" do + example "using a string and a block" do + allow_any_instance_of(Object).to receive_message_chain("foo.bar") { :baz } + expect(Object.new.foo.bar).to eq(:baz) + end + + example "using symbols and a hash" do + allow_any_instance_of(Object).to receive_message_chain(:foo, :bar => :baz) + expect(Object.new.foo.bar).to eq(:baz) + end + + example "using symbols and a block" do + allow_any_instance_of(Object).to receive_message_chain(:foo, :bar) { :baz } + expect(Object.new.foo.bar).to eq(:baz) + end + end + """ + When I run `rspec receive_message_chain_spec.rb` + Then the examples should all pass diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index e203a7158..5a2b500a3 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -38,6 +38,7 @@ Gem::Specification.new do |s| end s.add_development_dependency 'rake', '~> 10.0.0' - s.add_development_dependency 'cucumber', '~> 1.1.9' + s.add_development_dependency 'cucumber', '~> 1.3.15' s.add_development_dependency 'aruba', '~> 0.5' + s.add_development_dependency 'minitest', '~> 5.2' end From 24f8c6ba26a115b09f7b759da3357a55d14cdece Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 6 Jun 2014 11:29:41 -0700 Subject: [PATCH 0638/1179] Fix broken relish links. [ci skip] --- features/configuring_responses/README.md | 4 ++-- features/setting_constraints/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/configuring_responses/README.md b/features/configuring_responses/README.md index d593e7cab..25b731798 100644 --- a/features/configuring_responses/README.md +++ b/features/configuring_responses/README.md @@ -1,4 +1,4 @@ -When [allowing](../basics/allowing-messages) or [expecting](../basics/expecting-messages) messages, the default response is to return `nil`. Several +When [allowing](./basics/allowing-messages) or [expecting](./basics/expecting-messages) messages, the default response is to return `nil`. Several methods are provided to configure how the test double responds to the message. * `and_return` @@ -7,6 +7,6 @@ methods are provided to configure how the test double responds to the message. * `and_yield` * `and_call_original` -In addition, you can provide a [block implementation](./block-implementation) to respond in any manner you wish. +In addition, you can provide a [block implementation](./configuring-responses/block-implementation) to respond in any manner you wish. Note: for simplicity, the examples here use `allow` rather than `expect`, but these APIs apply equally to both cases. diff --git a/features/setting_constraints/README.md b/features/setting_constraints/README.md index 324da7587..d7d6c6296 100644 --- a/features/setting_constraints/README.md +++ b/features/setting_constraints/README.md @@ -2,5 +2,5 @@ RSpec provides a fluent interface off of `expect(...).to receive(...)` that allo further constrain what you expect: the arguments, the number of times, and the ordering of multiple messages. -Although not shown here, this fluent interface is also supported by [spies](../basics/spies), off of +Although not shown here, this fluent interface is also supported by [spies](./basics/spies), off of `have_received(...)`. From 607b9628cd5f4e9f87e019d3a15a89c2cfccc675 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 6 Jun 2014 11:34:41 -0700 Subject: [PATCH 0639/1179] Updated travis build scripts (from rspec-dev) --- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 14 +++++++++++++- script/run_build | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index dcb3ecc28..eccf74982 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-05-20T15:40:07+10:00 from the rspec-dev repo. +# This file was generated on 2014-06-06T11:34:33-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 66b0cf2a0..2a6c29c2f 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-05-20T15:40:07+10:00 from the rspec-dev repo. +# This file was generated on 2014-06-06T11:34:33-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 0640d4869..a533088af 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-05-20T15:40:07+10:00 from the rspec-dev repo. +# This file was generated on 2014-06-06T11:34:33-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html @@ -67,6 +67,14 @@ function documentation_enforced { fi } +function style_and_lint_enforced { + if [ -x ./bin/rubocop ]; then + return 0 + else + return 1 + fi +} + function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there travis_retry git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH @@ -142,3 +150,7 @@ function check_documentation_coverage { end " } + +function check_style_and_lint { + bin/rubocop lib +} diff --git a/script/run_build b/script/run_build index 47fb14c0b..1bfb23670 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-05-20T15:40:07+10:00 from the rspec-dev repo. +# This file was generated on 2014-06-06T11:34:33-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x @@ -12,6 +12,10 @@ if documentation_enforced; then check_documentation_coverage fi +if style_and_lint_enforced; then + check_style_and_lint +fi + if is_mri; then run_specs_one_by_one run_spec_suite_for "rspec-core" From ce70e97d6b322bcfba3542c4e0e61c8453b3840d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 6 Jun 2014 13:33:30 -0700 Subject: [PATCH 0640/1179] =?UTF-8?q?Reword=20changelog=20entry=20to=20exp?= =?UTF-8?q?lain=20why=20it=E2=80=99s=20a=20bug=20fix.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index b16d06aef..f9a3bb3fc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,8 +3,8 @@ Bug Fixes: -* Support constraining message chains by their arguments. (e.g. - `receive_message_chain(...).with(...)` ). (Jon Rowe, #697) +* Fix `receive_message_chain(...)` so that it supports `with` just like + `stub_chain` did. (Jon Rowe, #697) ### 3.0.0 / 2014-06-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.rc1...v3.0.0) From f7d15b4069d16a0fa982615de6546d4971015c24 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 6 Jun 2014 12:38:47 -0700 Subject: [PATCH 0641/1179] Fix regression in `expect_any_instance_of`. As of e00d6d7cf0f044bc9bd55d1defbbb433a88d67a4 it was (wrongly) acting like `expect_every_instance_of`. Fixes #699. --- Changelog.md | 3 +++ lib/rspec/mocks/any_instance/proxy.rb | 6 +++++- spec/rspec/mocks/any_instance_spec.rb | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index f9a3bb3fc..082a6f563 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,9 @@ Bug Fixes: * Fix `receive_message_chain(...)` so that it supports `with` just like `stub_chain` did. (Jon Rowe, #697) +* Fix regression in `expect_any_instance_of` so that it expects the + message on _any_ instance rather than on _every_ instance. + (Myron Marston, #699) ### 3.0.0 / 2014-06-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.rc1...v3.0.0) diff --git a/lib/rspec/mocks/any_instance/proxy.rb b/lib/rspec/mocks/any_instance/proxy.rb index 20c4519b7..5f7831c3b 100644 --- a/lib/rspec/mocks/any_instance/proxy.rb +++ b/lib/rspec/mocks/any_instance/proxy.rb @@ -60,7 +60,11 @@ def expect_chain(*chain, &block) def should_receive(method_name, &block) perform_proxying(__method__, [method_name], block) do |proxy| - proxy.add_message_expectation(method_name, &block) + # Yeah, this is a bit odd...but if we used `add_message_expectation` + # then it would act like `expect_every_instance_of(klass).to receive`. + # The any_instance recorder takes care of validating that an instance + # received the message. + proxy.add_stub(method_name, &block) end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 1d23f1487..63efa3fdb 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -459,6 +459,14 @@ def inspect expect(instance.foo(2)).to eq(2) end + it "does not set the expectation on every instance" do + # Setup an unrelated object of the same class that won't receive the expected message. + allow('non-related object').to receive(:non_related_method) + + expect_any_instance_of(Object).to receive(:foo) + 'something'.foo + end + it "does not modify the return value of stubs set on an instance" do expect_any_instance_of(Object).to receive(:foo).twice object = Object.new From 2fcfadf62d65499f9fab9b6d848c5f1577da8262 Mon Sep 17 00:00:00 2001 From: Jimmy Cuadra Date: Sat, 7 Jun 2014 13:08:19 -0700 Subject: [PATCH 0642/1179] Use RecursiveConstMethods from rspec-support. --- lib/rspec/mocks/mutate_const.rb | 84 ++------------------------- spec/rspec/mocks/mutate_const_spec.rb | 2 +- 2 files changed, 6 insertions(+), 80 deletions(-) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index 90a9fbf11..109f23f10 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -1,85 +1,11 @@ +RSpec::Support.require_rspec_support 'recursive_const_methods' + module RSpec module Mocks - # Provides recursive constant lookup methods useful for - # constant stubbing. - # - # @private - module RecursiveConstMethods - # We only want to consider constants that are defined directly on a - # particular module, and not include top-level/inherited constants. - # Unfortunately, the constant API changed between 1.8 and 1.9, so - # we need to conditionally define methods to ignore the top-level/inherited - # constants. - # - # Given: - # class A; B = 1; end - # class C < A; end - # - # On 1.8: - # - C.const_get("Hash") # => ::Hash - # - C.const_defined?("Hash") # => false - # - C.constants # => ["B"] - # - None of these methods accept the extra `inherit` argument - # On 1.9: - # - C.const_get("Hash") # => ::Hash - # - C.const_defined?("Hash") # => true - # - C.const_get("Hash", false) # => raises NameError - # - C.const_defined?("Hash", false) # => false - # - C.constants # => [:B] - # - C.constants(false) #=> [] - if Module.method(:const_defined?).arity == 1 - def const_defined_on?(mod, const_name) - mod.const_defined?(const_name) - end - - def get_const_defined_on(mod, const_name) - if const_defined_on?(mod, const_name) - return mod.const_get(const_name) - end - - raise NameError, "uninitialized constant #{mod.name}::#{const_name}" - end - - def constants_defined_on(mod) - mod.constants.select { |c| const_defined_on?(mod, c) } - end - else - def const_defined_on?(mod, const_name) - mod.const_defined?(const_name, false) - end - - def get_const_defined_on(mod, const_name) - mod.const_get(const_name, false) - end - - def constants_defined_on(mod) - mod.constants(false) - end - end - - def recursive_const_get(const_name) - normalize_const_name(const_name).split('::').inject(Object) do |mod, name| - get_const_defined_on(mod, name) - end - end - - def recursive_const_defined?(const_name) - normalize_const_name(const_name).split('::').inject([Object, '']) do |(mod, full_name), name| - yield(full_name, name) if block_given? && !(Module === mod) - return false unless const_defined_on?(mod, name) - [get_const_defined_on(mod, name), [mod, name].join('::')] - end - end - - def normalize_const_name(const_name) - const_name.sub(/\A::/, '') - end - end - # Provides information about constants that may (or may not) # have been mutated by rspec-mocks. class Constant - extend RecursiveConstMethods + extend Support::RecursiveConstMethods # @api private def initialize(name) @@ -154,7 +80,7 @@ def self.original(name) # Provides a means to stub constants. class ConstantMutator - extend RecursiveConstMethods + extend Support::RecursiveConstMethods # Stubs a constant. # @@ -196,7 +122,7 @@ def self.hide(constant_name) # # @private class BaseMutator - include RecursiveConstMethods + include Support::RecursiveConstMethods attr_reader :original_value, :full_constant_name diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 258305083..157ea966b 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -24,7 +24,7 @@ class TestSubClass < TestClass module RSpec module Mocks describe "Constant Mutating" do - include RSpec::Mocks::RecursiveConstMethods + include RSpec::Support::RecursiveConstMethods def reset_rspec_mocks ::RSpec::Mocks.space.reset_all From 8152a6aa6029362ec7c52ce573238b9d494aad90 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 7 Jun 2014 14:54:05 -0700 Subject: [PATCH 0643/1179] Release 3.0.1. --- Changelog.md | 4 ++-- lib/rspec/mocks/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 082a6f563..16c60845d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.0.1 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...master) +### 3.0.1 /2014-06-07 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1) Bug Fixes: diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index eabdbcb71..add079976 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.0.0' + STRING = '3.0.1' end end end From 9a49ffd8d91b3594e6eae7efd038ed981263e7ef Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 7 Jun 2014 14:58:01 -0700 Subject: [PATCH 0644/1179] Fix typo. [ci skip] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 16c60845d..8c8a5344a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 3.0.1 /2014-06-07 +### 3.0.1 / 2014-06-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1) Bug Fixes: From 266a07f007e841c1c542e98204ac3beb5047879e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 7 Jun 2014 15:13:31 -0700 Subject: [PATCH 0645/1179] Put the scope cuke last in the basics section. It was odd for it to be first. [ci skip] --- features/.nav | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/.nav b/features/.nav index 63ad3e3a5..77f4a4ac3 100644 --- a/features/.nav +++ b/features/.nav @@ -1,11 +1,11 @@ - basics: - - scope.feature - test_doubles.feature - allowing_messages.feature - expecting_messages.feature - partial_test_doubles.feature - null_object_doubles.feature - spies.feature + - scope.feature - verifying_doubles: - instance_doubles.feature - class_doubles.feature From 59d786dda6253b7bdf93e1aa1e9499995f9785d5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 9 Jun 2014 08:13:16 -0700 Subject: [PATCH 0646/1179] Use maintenance branch. --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 53e87a698..0c9b59edf 100644 --- a/Gemfile +++ b/Gemfile @@ -2,12 +2,13 @@ source "https://rubygems.org" gemspec +branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp %w[rspec rspec-core rspec-expectations rspec-support].each do |lib| library_path = File.expand_path("../../#{lib}", __FILE__) if File.exist?(library_path) && !ENV['USE_GIT_REPOS'] gem lib, :path => library_path else - gem lib, :git => "git://github.com/rspec/#{lib}.git" + gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => branch end end From 0fd22663357dbb3ba55c11913dfbb04fbbcfcec7 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sat, 10 May 2014 14:02:21 +0100 Subject: [PATCH 0647/1179] Adds spying methods to the RSpec Mocks DSL Specifically adds: * spy - responds and spies all methods * instance_spy - responds and spies all methods to which a specified class responds. * object_spy - responds and spies all methods to which a specific instance responds * class_spy - responds and spies all class methods to which a specific class responds I added test coverage for the expected behaviours and added some YARD docs to the new methods. --- Changelog.md | 9 +++ README.md | 22 +++++- features/basics/spies.feature | 20 ++--- lib/rspec/mocks/error_generator.rb | 4 +- lib/rspec/mocks/example_methods.rb | 72 +++++++++++++++++- spec/rspec/mocks/spy_spec.rb | 115 +++++++++++++++++++++++++++++ 6 files changed, 224 insertions(+), 18 deletions(-) create mode 100644 spec/rspec/mocks/spy_spec.rb diff --git a/Changelog.md b/Changelog.md index 8c8a5344a..33d8b1f83 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 3.1.0 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...master) + +Enhancements: + +* Add spying methods (`spy`, `ìnstance_spy`, `class_spy` and `object_spy`) + which create doubles as null objects for use with spying in testing. (Sam + Phippen, #671) + ### 3.0.1 / 2014-06-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1) diff --git a/README.md b/README.md index 4bb30fa25..63a3d20db 100644 --- a/README.md +++ b/README.md @@ -101,15 +101,24 @@ zipcode.valid? ## Test Spies -Verifies the given object received the expected message during the course of the -test. The method must have previously been stubbed in order for messages to be -verified. +Verifies the given object received the expected message during the course of +the test. For a message to be verified, the given object must be setup to spy +on it, either by having it explicitly stubbed or by being a null object double +(e.g. `double(...).as_null_object`). Convenience methods are provided to easily +create null object doubles for this purpose: + +```ruby +spy("invitation") # => same as `double("invitiation").as_null_object` +instance_spy("Invitation") # => same as `instance_double("Invitiation").as_null_object` +class_spy("Invitation") # => same as `class_double("Invitiation").as_null_object` +object_spy("Invitation") # => same as `object_double("Invitiation").as_null_object` +``` Stubbing and verifying messages received in this way implements the Test Spy pattern. ```ruby - invitation = double('invitation', :accept => true) + invitation = spy('invitation') user.accept_invitation(invitation) @@ -119,6 +128,11 @@ pattern. expect(invitation).to have_received(:accept).with(mailer) expect(invitation).to have_received(:accept).twice expect(invitation).to_not have_received(:accept).with(mailer) + + # One can specify a return value on the spy the same way one would a double. + invitation = spy('invitation', :accept => true) + expect(invitation).to have_received(:accept).with(mailer) + expect(invitation.accept).to eq(true) ``` ## Nomenclature diff --git a/features/basics/spies.feature b/features/basics/spies.feature index 78e2ffd8f..195362a04 100644 --- a/features/basics/spies.feature +++ b/features/basics/spies.feature @@ -7,25 +7,25 @@ Feature: Spies `have_received`. You can use any test double (or partial double) as a spy, but the double must be setup to - spy on the messages you care about. [Null object doubles](./null-object-doubles) automatically spy on all messages, + spy on the messages you care about. Spies automatically spy on all messages, or you can [allow a message](./allowing-messages) to spy on it. `have_received` supports the same fluent interface for [setting constraints](../setting-constraints) that normal message expectations do. Note: The `have_received` API shown here will only work if you are using rspec-expectations. - Scenario: Use a null object double as a spy - Given a file named "null_object_spy_spec.rb" with: + Scenario: Using a spy + Given a file named "spy_spec.rb" with: """ruby RSpec.describe "have_received" do it "passes when the message has been received" do - invitation = double('invitation').as_null_object + invitation = spy('invitation') invitation.deliver expect(invitation).to have_received(:deliver) end end """ - When I run `rspec null_object_spy_spec.rb` + When I run `rspec spy_spec.rb` Then the examples should all pass Scenario: Spy on a method on a partial double @@ -54,8 +54,8 @@ Feature: Spies end RSpec.describe "failure when the message has not been received" do - example "for a null object double" do - invitation = double('invitation').as_null_object + example "for a spy" do + invitation = spy('invitation') expect(invitation).to have_received(:deliver) end @@ -68,7 +68,7 @@ Feature: Spies When I run `rspec failure_spec.rb --order defined` Then it should fail with: """ - 1) failure when the message has not been received for a null object double + 1) failure when the message has not been received for a spy Failure/Error: expect(invitation).to have_received(:deliver) (Double "invitation").deliver(any args) expected: 1 time with any arguments @@ -87,7 +87,7 @@ Feature: Spies Given a file named "setting_constraints_spec.rb" with: """ruby RSpec.describe "An invitiation" do - let(:invitation) { double("invitation").as_null_object } + let(:invitation) { spy("invitation") } before do invitation.deliver("foo@example.com") @@ -131,7 +131,7 @@ Feature: Spies Given a file named "generates_description_spec.rb" with: """ruby RSpec.describe "An invitation" do - subject(:invitation) { double('invitation').as_null_object } + subject(:invitation) { spy('invitation') } before { invitation.deliver } it { is_expected.to have_received(:deliver) } end diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 0092c2ee6..6db832673 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -169,13 +169,13 @@ def raise_only_valid_on_a_partial_double(method) # @private def raise_expectation_on_unstubbed_method(method) __raise "#{intro} expected to have received #{method}, but that " + - "method has not been stubbed." + "object is not a spy or method has not been stubbed." end # @private def raise_expectation_on_mocked_method(method) __raise "#{intro} expected to have received #{method}, but that " + - "method has been mocked instead of stubbed." + "method has been mocked instead of stubbed or spied." end def self.raise_double_negation_error(wrapped_expression) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 1bf149227..1957c1200 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -84,6 +84,73 @@ def object_double(object_or_name, *args) ExampleMethods.declare_verifying_double(ObjectVerifyingDouble, ref, *args) end + # @overload spy() + # @overload spy(name) + # @param name [String/Symbol] used to clarify intent + # @overload spy(stubs) + # @param stubs (Hash) hash of message/return-value pairs + # @overload spy(name, stubs) + # @param name [String/Symbol] used to clarify intent + # @param stubs (Hash) hash of message/return-value pairs + # @return (Double) + # + # Constructs a test double that is optimized for use with + # `have_received`. With a normal double one has to stub methods in order + # to be able to spy them. A spy automatically spies on all methods. + def spy(*args) + double(*args).as_null_object + end + + # @overload instance_spy(doubled_class) + # @param doubled_class [String, Class] + # @overload instance_spy(doubled_class, stubs) + # @param doubled_class [String, Class] + # @param stubs [Hash] hash of message/return-value pairs + # @return InstanceVerifyingDouble + # + # Constructs a test double that is optimized for use with `have_received` + # against a specific class. If the given class name has been loaded, only + # instance methods defined on the class are allowed to be stubbed. With + # a normal double one has to stub methods in order to be able to spy + # them. An instance_spy automatically spies on all instance methods to + # which the class responds. + def instance_spy(*args) + instance_double(*args).as_null_object + end + + # @overload object_spy(object_or_name) + # @param object_or_name [String, Object] + # @overload object_spy(object_or_name, stubs) + # @param object_or_name [String, Object] + # @param stubs [Hash] hash of message/return-value pairs + # @return ObjectVerifyingDouble + # + # Constructs a test double that is optimized for use with `have_received` + # against a specific object. Only instance methods defined on the object + # are allowed to be stubbed. With a normal double one has to stub + # methods in order to be able to spy them. An object_spy automatically + # spies on all methods to which the object responds. + def object_spy(*args) + object_double(*args).as_null_object + end + + # @overload class_spy(doubled_class) + # @param doubled_class [String, Module] + # @overload class_spy(doubled_class, stubs) + # @param doubled_class [String, Module] + # @param stubs [Hash] hash of message/return-value pairs + # @return ClassVerifyingDouble + # + # Constructs a test double that is optimized for use with `have_received` + # against a specific class. If the given class name has been loaded, + # only class methods defined on the class are allowed to be stubbed. + # With a normal double one has to stub methods in order to be able to spy + # them. An class_spy automatically spies on all class methods to which the + # class responds. + def class_spy(*args) + class_double(*args).as_null_object + end + # Disables warning messages about expectations being set on nil. # # By default warning messages are issued when expectations are set on @@ -151,8 +218,9 @@ def hide_const(constant_name) end # Verifies that the given object received the expected message during the - # course of the test. The method must have previously been stubbed in - # order for messages to be verified. + # course of the test. On a spy objects or as null object doubles this + # works for any method, on other objects the method must have + # been stubbed beforehand in order for messages to be verified. # # Stubbing and verifying messages received in this way implements the # Test Spy pattern. diff --git a/spec/rspec/mocks/spy_spec.rb b/spec/rspec/mocks/spy_spec.rb new file mode 100644 index 000000000..02650f319 --- /dev/null +++ b/spec/rspec/mocks/spy_spec.rb @@ -0,0 +1,115 @@ +require "spec_helper" + +describe "the spy family of methods" do + describe "spy" do + it "responds to arbitrary methods" do + expect(spy.respond_to?(:foo)).to be true + end + + it "takes a name" do + expect(spy(:bacon_bits).inspect).to include("bacon_bits") + end + + it "records called methods" do + expect(spy.tap { |s| s.foo }).to have_received(:foo) + end + + it "takes a hash of method names and return values" do + expect(spy(:foo => :bar).foo).to eq(:bar) + end + + it "takes a name and a hash of method names and return values" do + expect(spy(:bacon_bits, :foo => :bar).foo).to eq(:bar) + end + end + + shared_examples_for "a verifying spy with a foo method" do + it "responds to methods on the verified object" do + expect(subject.respond_to?(:foo)).to be true + end + + it "does not respond to methods that are not on the verified object" do + expect(subject.respond_to?(:other_method)).to be false + end + + it "records called methods" do + expect(subject.tap { |s| s.foo}).to have_received(:foo) + end + end + + describe "instance_spy" do + context "when passing a class object" do + let(:the_class) do + Class.new do + def foo + 3 + end + end + end + + subject { instance_spy(the_class) } + + it_behaves_like "a verifying spy with a foo method" + + it "takes a class and a hash of method names and return values" do + expect(instance_spy(the_class, :foo => :bar).foo).to eq(:bar) + end + end + + context "passing a class by string reference" do + DummyClass = Class.new do + def foo + 3 + end + end + + let(:the_class) { "DummyClass" } + + subject { instance_spy(the_class) } + + it_behaves_like "a verifying spy with a foo method" + + it "takes a class name string and a hash of method names and return values" do + expect(instance_spy(the_class, :foo => :bar).foo).to eq(:bar) + end + end + end + + describe "object_spy" do + let(:the_class) do + Class.new do + def foo + 3 + end + end + end + + let(:the_instance) { the_class.new } + + subject { object_spy(the_instance) } + + it_behaves_like "a verifying spy with a foo method" + + it "takes an instance and a hash of method names and return values" do + expect(object_spy(the_instance, :foo => :bar).foo).to eq(:bar) + end + end + + describe "class_spy" do + let(:the_class) do + Class.new do + def self.foo + 3 + end + end + end + + subject { class_spy(the_class) } + + it_behaves_like "a verifying spy with a foo method" + + it "takes a class and a hash of method names and return values" do + expect(class_spy(the_class, :foo => :bar).foo).to eq(:bar) + end + end +end From e7af3978ff22a343682851dfa4887411b905b3ae Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 12 Jun 2014 10:22:46 -0700 Subject: [PATCH 0648/1179] 2.99.1 release notes. [ci skip] --- Changelog.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Changelog.md b/Changelog.md index 33d8b1f83..3d1ac0564 100644 --- a/Changelog.md +++ b/Changelog.md @@ -224,6 +224,18 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.1 / 2014-06-12 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0...v2.99.1) + +Bug Fixes: + +* Fix bug that caused errors at the end of each example + when a `double.as_null_object` had been frozen. (Yuji Nakayama, #698) + +Deprecations: + +* Deprecate freezing a test double. (Yuji Nakayama, #698) + ### 2.99.0 / 2014-06-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.rc1...v2.99.0) From a9d2100f85a22e752b4d8f668dbc80a7be80e646 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 12 Jun 2014 19:23:02 +0100 Subject: [PATCH 0649/1179] =?UTF-8?q?Fix=20edge=20case=20that=20triggered?= =?UTF-8?q?=20=E2=80=9Ccan't=20add=20a=20new=20key=20into=20hash=20during?= =?UTF-8?q?=20iteration=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was triggered when a double was used as a `with` arg but was otherwise unused — in this case, there was no mock proxy for it, but while verifying one would get created (due to internal implementation details). Doing so in a `each_value` loop caused the error. --- lib/rspec/mocks/space.rb | 2 +- spec/rspec/mocks/double_spec.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index b9a649b43..55e912201 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -65,7 +65,7 @@ def new_scope end def verify_all - proxies.each_value { |proxy| proxy.verify } + proxies.values.each { |proxy| proxy.verify } any_instance_recorders.each_value { |recorder| recorder.verify } end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index e61ed760d..baae8e1ee 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -35,6 +35,15 @@ module Mocks expect { reset dbl }.not_to raise_error end + it "generates the correct error when an unfulfilled expectation uses an unused double as a `with` argument" do + expect { + a = double('a') + b = double('b') + expect(a).to receive(:append).with(b) + verify_all + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + it 'allows string representation of methods in constructor' do dbl = double('foo' => 1) expect(dbl.foo).to eq(1) From 4c0839a88f3157152f53d6e90eaeefe6f050b1af Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 12 Jun 2014 13:30:13 -0700 Subject: [PATCH 0650/1179] Add changelog for #711. [ci skip] --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3d1ac0564..fb6bb6d67 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,14 @@ Enhancements: which create doubles as null objects for use with spying in testing. (Sam Phippen, #671) +### 3.0.2 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.1...3-0-maintenance) + +Bug Fixes: + +* Fix edge case that triggered “can't add a new key into hash during + iteration" during mock verification. (Sam Phippen, Myron Marston, #711) + ### 3.0.1 / 2014-06-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1) From e21dc8b62f5c1bde5e2651dce24263e5b5aaec91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Martell=20N=C3=BA=C3=B1ez?= Date: Sat, 14 Jun 2014 12:00:20 +0100 Subject: [PATCH 0651/1179] Fix invitation typo in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 63a3d20db..1b845f786 100644 --- a/README.md +++ b/README.md @@ -108,10 +108,10 @@ on it, either by having it explicitly stubbed or by being a null object double create null object doubles for this purpose: ```ruby -spy("invitation") # => same as `double("invitiation").as_null_object` -instance_spy("Invitation") # => same as `instance_double("Invitiation").as_null_object` -class_spy("Invitation") # => same as `class_double("Invitiation").as_null_object` -object_spy("Invitation") # => same as `object_double("Invitiation").as_null_object` +spy("invitation") # => same as `double("invitation").as_null_object` +instance_spy("Invitation") # => same as `instance_double("Invitation").as_null_object` +class_spy("Invitation") # => same as `class_double("Invitation").as_null_object` +object_spy("Invitation") # => same as `object_double("Invitation").as_null_object` ``` Stubbing and verifying messages received in this way implements the Test Spy From 99af012f91edd5da8ac92ea569c5fca6f205f244 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 14 Jun 2014 09:40:59 -0700 Subject: [PATCH 0652/1179] Bump version to 3.1.0.pre to prevent confusion with master being 3.0.0 but 3.0.x releases are out. --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index add079976..6c8d74638 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.0.1' + STRING = '3.1.0.pre' end end end From e3967e6340193851ab9bf82f617ffcfe1d30716c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 16 Jun 2014 17:20:49 -0700 Subject: [PATCH 0653/1179] Remove extra `once`. --- spec/rspec/mocks/double_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index baae8e1ee..824b04701 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -591,9 +591,9 @@ def initialize(amount, units) end it "fails when calling yielding method consecutively with wrong arity" do - expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). - and_yield('down'). - and_yield(14, 65) + expect(@double).to receive(:yield_back).once.with(no_args()).and_yield('wha', 'zup'). + and_yield('down'). + and_yield(14, 65) expect { c = [] @double.yield_back {|a,b| c << [a, b]} From 5ccd473384b47e6b4ab9c97dfd11144f9963ac22 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 17 Jun 2014 10:24:45 -0700 Subject: [PATCH 0654/1179] Fix typo. [ci skip] --- lib/rspec/mocks/configuration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 3ad93482e..35595d579 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -22,7 +22,7 @@ def yield_receiver_to_any_instance_implementation_blocks? # @example # # RSpec.configure do |rspec| - # rspec.mock_with :rspc do |mocks| + # rspec.mock_with :rspec do |mocks| # mocks.yield_receiver_to_any_instance_implementation_blocks = false # end # end From 8f50ed4594eda8f6d29dce04a8b1024c182e0742 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 18 Jun 2014 16:34:52 -0700 Subject: [PATCH 0655/1179] Verifying doubles should expire just like normal doubles. --- Changelog.md | 3 + lib/rspec/mocks/space.rb | 2 +- lib/rspec/mocks/test_double.rb | 8 ++- lib/rspec/mocks/verifying_double.rb | 12 ++-- lib/rspec/mocks/verifying_proxy.rb | 2 +- spec/rspec/mocks/double_spec.rb | 54 ----------------- spec/rspec/mocks/expiration_spec.rb | 90 +++++++++++++++++++++++++++++ 7 files changed, 108 insertions(+), 63 deletions(-) create mode 100644 spec/rspec/mocks/expiration_spec.rb diff --git a/Changelog.md b/Changelog.md index fb6bb6d67..665e874df 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,9 @@ Bug Fixes: * Fix edge case that triggered “can't add a new key into hash during iteration" during mock verification. (Sam Phippen, Myron Marston, #711) +* Fix verifying doubles so that when they accidentally leak into another + example, they provide the same clear error message that normal doubles + do. (Myron Marston, #718) ### 3.0.1 / 2014-06-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 55e912201..73ceae9eb 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -148,7 +148,7 @@ def self.synchronize def proxy_not_found_for(id, object) proxies[id] = case object when NilClass then ProxyForNil.new(@expectation_ordering) - when TestDouble then object.__build_mock_proxy(@expectation_ordering) + when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering) when Class if RSpec::Mocks.configuration.verify_partial_doubles? VerifyingPartialClassDoubleProxy.new(self, object, @expectation_ordering) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index f59f8e385..74bfeffa8 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -53,8 +53,8 @@ def respond_to?(message, incl_private=false) end # @private - def __build_mock_proxy(order_group) - __raise_expired_error or TestDoubleProxy.new(self, order_group, @name) + def __build_mock_proxy_unless_expired(order_group) + __raise_expired_error or __build_mock_proxy(order_group) end # @private @@ -111,6 +111,10 @@ def __mock_proxy ::RSpec::Mocks.space.proxy_for(self) end + def __build_mock_proxy(order_group) + TestDoubleProxy.new(self, order_group, @name) + end + def __raise_expired_error return false unless @__expired ErrorGenerator.new(self, @name).raise_expired_test_double_error diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 3a3d288d7..ca1e1ad20 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -88,6 +88,13 @@ module ObjectVerifyingDoubleMethods include TestDouble include VerifyingDouble + def as_stubbed_const(options = {}) + ConstantMutator.stub(@doubled_module.const_to_replace, self, options) + self + end + + private + def initialize(doubled_module, *args) @doubled_module = doubled_module super(doubled_module.description, *args) @@ -99,11 +106,6 @@ def __build_mock_proxy(order_group) ObjectMethodReference ) end - - def as_stubbed_const(options = {}) - ConstantMutator.stub(@doubled_module.const_to_replace, self, options) - self - end end # Similar to an InstanceVerifyingDouble, except that it verifies against diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 99b18a8ec..f4cd3ab70 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -54,7 +54,7 @@ def ensure_publicly_implemented(method_name, object) # isolation. # # @private - class VerifyingProxy < Proxy + class VerifyingProxy < TestDoubleProxy include VerifyingProxyMethods def initialize(object, order_group, name, doubled_module, method_reference_class) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 824b04701..17f81290c 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -74,60 +74,6 @@ module Mocks expect(dbl.send(:some_msg)).to eq("received") end - context "after it has been torn down" do - let(:dbl) { double } - - before do - expect(dbl).to receive(:foo).at_least(:once) - allow(dbl).to receive(:bar) - dbl.foo - - RSpec::Mocks.verify - RSpec::Mocks.teardown - RSpec::Mocks.setup - end - - it 'disallows previously mocked methods' do - expect { dbl.foo }.to raise_error(ExpiredTestDoubleError) - end - - it 'disallows previously stubbed methods' do - expect { dbl.bar }.to raise_error(ExpiredTestDoubleError) - end - - it 'disallows stubbing new methods (with receive)' do - expect { - allow(dbl).to receive(:bazz) - }.to raise_error(ExpiredTestDoubleError) - end - - it 'disallows stubbing new methods (with receive_messages)' do - expect { - allow(dbl).to receive_messages(:bazz => 3) - }.to raise_error(ExpiredTestDoubleError) - end - - it 'disallows stubbing new message chains' do - expect { - allow(dbl).to receive_message_chain(:bazz, :bam, :goo) - }.to raise_error(ExpiredTestDoubleError) - end - - it 'disallows mocking new methods' do - expect { - expect(dbl).to receive(:bazz) - }.to raise_error(ExpiredTestDoubleError) - end - - it 'disallows being turned into a null object' do - expect { dbl.as_null_object }.to raise_error(ExpiredTestDoubleError) - end - - it 'disallows being checked for nullness' do - expect { dbl.null_object? }.to raise_error(ExpiredTestDoubleError) - end - end - it "reports line number of expectation of unreceived message" do expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) expect { diff --git a/spec/rspec/mocks/expiration_spec.rb b/spec/rspec/mocks/expiration_spec.rb new file mode 100644 index 000000000..5d93345c8 --- /dev/null +++ b/spec/rspec/mocks/expiration_spec.rb @@ -0,0 +1,90 @@ +module RSpec + module Mocks + RSpec.describe "After a test double has been torn down" do + shared_examples_for "expiration" do + before do + expect(dbl).to receive(:foo).at_least(:once) + allow(dbl).to receive(:bar) + dbl.foo + + RSpec::Mocks.verify + RSpec::Mocks.teardown + RSpec::Mocks.setup + end + + it 'disallows previously mocked methods' do + expect { dbl.foo }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows previously stubbed methods' do + expect { dbl.bar }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows stubbing new methods (with receive)' do + expect { + allow(dbl).to receive(:bazz) + }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows stubbing new methods (with receive_messages)' do + expect { + allow(dbl).to receive_messages(:bazz => 3) + }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows stubbing new message chains' do + expect { + allow(dbl).to receive_message_chain(:bazz, :bam, :goo) + }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows mocking new methods' do + expect { + expect(dbl).to receive(:bazz) + }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows being turned into a null object' do + expect { dbl.as_null_object }.to raise_error(ExpiredTestDoubleError) + end + + it 'disallows being checked for nullness' do + expect { dbl.null_object? }.to raise_error(ExpiredTestDoubleError) + end + + end + + context "for a plain double" do + let(:dbl) { double } + include_examples "expiration" + end + + class ExpiredInstanceInterface + def foo; end + def bar; end + def bazz; end + end + + class ExpiredClassInterface + def self.foo; end + def self.bar; end + def self.bazz; end + end + + context "for an instance_double" do + let(:dbl) { instance_double(ExpiredInstanceInterface) } + include_examples "expiration" + end + + context "for a class_double" do + let(:dbl) { class_double(ExpiredClassInterface) } + include_examples "expiration" + end + + context "for an object_double" do + let(:dbl) { object_double(ExpiredInstanceInterface.new) } + include_examples "expiration" + end + end + end +end From 6a9bdf061bd0dc0e17339e0e95c8edff7a8a84af Mon Sep 17 00:00:00 2001 From: Thomas Brand Date: Fri, 13 Jun 2014 22:52:01 +0000 Subject: [PATCH 0656/1179] `and_yield` not using default block argument when no argument given. Using ruby 2.1.2 ```ruby default_arg = Object.new obj = Object.new allow(obj).to receive(:a_message).and_yield expect(default_arg).to receive(:bar) obj.a_message do |receiver=default_arg| receiver.bar end ``` The above code fails with: ``` Failure/Error: obj.a_message do |receiver=default_arg| # yielded || to block with arity of 1 ``` The block has a arity of 0 or 1 not 1. So I would expect that when no argument is given to `and_yield` it would use the default block argument. --- spec/rspec/mocks/and_yield_spec.rb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index 52068254c..b5011c39f 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -99,6 +99,33 @@ verify yielded_arg end + context "that are optional" do + it "yields the default argument when the argument is not given" do + default_arg = Object.new + obj = Object.new + + allow(obj).to receive(:a_message).and_yield + expect(default_arg).to receive(:bar) + + obj.a_message do |receiver=default_arg| + receiver.bar + end + end + + it "yields given argument when the argument is given" do + default_arg = Object.new + given_arg = Object.new + obj = Object.new + + allow(obj).to receive(:a_message).and_yield(given_arg) + expect(given_arg).to receive(:bar) + + obj.a_message do |receiver=default_arg| + receiver.bar + end + end + end + it "fails when expectations set on the eval context and yielded arguments are not met" do configured_eval_context = nil yielded_arg = Object.new From b99a73bc4205f51984b135d2570e27549d618a19 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 19 Jun 2014 07:25:49 -0700 Subject: [PATCH 0657/1179] =?UTF-8?q?1.8.7=20can=E2=80=99t=20parse=20block?= =?UTF-8?q?s=20with=20default=20args.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/rspec/mocks/and_yield_spec.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index b5011c39f..0a24fa06e 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -99,7 +99,7 @@ verify yielded_arg end - context "that are optional" do + context "that are optional", :if => RSpec::Support::RubyFeatures.optional_and_splat_args_supported? do it "yields the default argument when the argument is not given" do default_arg = Object.new obj = Object.new @@ -107,9 +107,7 @@ allow(obj).to receive(:a_message).and_yield expect(default_arg).to receive(:bar) - obj.a_message do |receiver=default_arg| - receiver.bar - end + eval("obj.a_message { |receiver=default_arg| receiver.bar }") end it "yields given argument when the argument is given" do @@ -120,9 +118,7 @@ allow(obj).to receive(:a_message).and_yield(given_arg) expect(given_arg).to receive(:bar) - obj.a_message do |receiver=default_arg| - receiver.bar - end + eval("obj.a_message { |receiver=default_arg| receiver.bar }") end end From 1886ad2d3dedd112c778176ca88e0961b6e0789a Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 13 Jun 2014 12:22:01 +0100 Subject: [PATCH 0658/1179] Fix exact receive counts with ordered --- lib/rspec/mocks/message_expectation.rb | 11 +++ .../mocks/matchers/have_received_spec.rb | 60 ++++++++++++++++ spec/rspec/mocks/matchers/receive_spec.rb | 70 +++++++++++++++++++ 3 files changed, 141 insertions(+) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 8b21bce11..7c7ce6494 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -59,6 +59,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @argument_list_matcher = ArgumentListMatcher::MATCH_ALL @order_group = expectation_ordering @order_group.register(self) unless type == :stub + @expectation_type = type @ordered = false @at_least = @at_most = @exactly = nil @args_to_yield = [] @@ -438,10 +439,20 @@ def twice(&block) # expect(api).to receive(:finish).ordered def ordered(&block) self.inner_implementation_action = block + additional_expected_calls.times do + @order_group.register(self) + end @ordered = true self end + # @private + def additional_expected_calls + return 0 if @expectation_type == :stub || !@exactly + @expected_received_count - 1 + end + + # @private def ordered? @ordered diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 9e5e7c432..64d9be800 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -264,6 +264,66 @@ module Mocks expect(dbl).to have_received(:two).ordered end + it 'passes with exact receive counts when received in order' do + dbl.one + dbl.one + dbl.two + + expect(dbl).to have_received(:one).twice.ordered + expect(dbl).to have_received(:two).once.ordered + end + + pending 'passes with at most receive counts when received in order' do + dbl.one + dbl.one + dbl.two + + expect(dbl).to have_received(:one).at_most(3).times.ordered + expect(dbl).to have_received(:two).once.ordered + end + + pending 'passes with at least receive counts when received in order' do + dbl.one + dbl.one + dbl.two + + expect(dbl).to have_received(:one).at_least(1).times.ordered + expect(dbl).to have_received(:two).once.ordered + end + + it 'fails with exact receive counts when received out of order' do + dbl.one + dbl.two + dbl.one + + expect { + expect(dbl).to have_received(:one).twice.ordered + expect(dbl).to have_received(:two).once.ordered + }.to raise_error(/received :two out of order/m) + end + + pending "fails with at most receive counts when recieved out of order" do + dbl.one + dbl.two + dbl.one + + expect { + expect(dbl).to have_received(:one).at_most(2).times.ordered + expect(dbl).to have_received(:two).once.ordered + }.to raise_error(/received :two out of order/m) + end + + pending "fails with at least receive counts when recieved out of order" do + dbl.one + dbl.two + dbl.one + + expect { + expect(dbl).to have_received(:one).at_least(1).times.ordered + expect(dbl).to have_received(:two).once.ordered + }.to raise_error(/received :two out of order/m) + end + it 'fails when the messages are received out of order' do dbl.two dbl.one diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index f44a591a4..749b26522 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -300,6 +300,76 @@ def receiver.method_missing(*a); end # a poor man's stub... it_behaves_like "resets partial mocks cleanly" do let(:target) { expect(object) } end + + context "ordered with receive counts" do + let(:dbl) { double(:one => 1, :two => 2) } + + it "passes with exact receive counts when the ordering is correct" do + expect(dbl).to receive(:one).twice.ordered + expect(dbl).to receive(:two).once.ordered + + dbl.one + dbl.one + dbl.two + end + + it "fails with exact receive counts when the ordering is incorrect" do + expect { + expect(dbl).to receive(:one).twice.ordered + expect(dbl).to receive(:two).once.ordered + + dbl.one + dbl.two + dbl.one + }.to raise_error(/out of order/) + + reset_all + end + + it "passes with at least when the ordering is correct" do + expect(dbl).to receive(:one).at_least(2).times.ordered + expect(dbl).to receive(:two).once.ordered + + dbl.one + dbl.one + dbl.one + dbl.two + end + + pending "fails with at least when the ordering is incorrect" do + expect { + expect(dbl).to receive(:one).at_least(2).times.ordered + expect(dbl).to receive(:two).once.ordered + + dbl.one + dbl.two + }.to raise_error + + reset_all + end + + it "passes with at most when the ordering is correct" do + expect(dbl).to receive(:one).at_most(2).times.ordered + expect(dbl).to receive(:two).once.ordered + + dbl.one + dbl.two + end + + pending "fails with at most when the ordering is incorrect" do + expect { + expect(dbl).to receive(:one).at_most(2).times.ordered + expect(dbl).to receive(:two).once.ordered + + dbl.one + dbl.one + dbl.one + dbl.two + }.to raise_error + + reset_all + end + end end describe "expect_any_instance_of(...).to receive" do From 6b4ff32be0c3eebafe987b85af1ae53eaa7464cb Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 19 Jun 2014 09:31:07 +0200 Subject: [PATCH 0659/1179] Add a changelog entry for #713 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 665e874df..ba0134e39 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,8 @@ Bug Fixes: * Fix verifying doubles so that when they accidentally leak into another example, they provide the same clear error message that normal doubles do. (Myron Marston, #718) +* Make `ordered` work with exact receive counts. (Sam Phippem, #713) + ### 3.0.1 / 2014-06-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1) From 58a8f69ff9f0868629f44c635e762354c51114ec Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 19 Jun 2014 12:19:54 -0700 Subject: [PATCH 0660/1179] =?UTF-8?q?Don=E2=80=99t=20use=20smart=20quotes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ci skip] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 665e874df..5b3c9f2a6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,7 +12,7 @@ Enhancements: Bug Fixes: -* Fix edge case that triggered “can't add a new key into hash during +* Fix edge case that triggered "can't add a new key into hash during iteration" during mock verification. (Sam Phippen, Myron Marston, #711) * Fix verifying doubles so that when they accidentally leak into another example, they provide the same clear error message that normal doubles From b834997441f0e454b7fa1e58f40f3648e7e003bd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 19 Jun 2014 12:24:24 -0700 Subject: [PATCH 0661/1179] Changelog fixups. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove extra blank line. - Fix spelling on Sam’s name. [ci skip] --- Changelog.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index e171310d2..c92253dd2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,8 +17,7 @@ Bug Fixes: * Fix verifying doubles so that when they accidentally leak into another example, they provide the same clear error message that normal doubles do. (Myron Marston, #718) -* Make `ordered` work with exact receive counts. (Sam Phippem, #713) - +* Make `ordered` work with exact receive counts. (Sam Phippen, #713) ### 3.0.1 / 2014-06-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1) From 427e85c122c17ae41ac17895e47e49218962b1f5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 19 Jun 2014 20:36:18 -0700 Subject: [PATCH 0662/1179] Forwardport 3.0.2 release notes. [ci skip] --- Changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index c92253dd2..489ed7ef4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.1.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.2...master) Enhancements: @@ -7,8 +7,8 @@ Enhancements: which create doubles as null objects for use with spying in testing. (Sam Phippen, #671) -### 3.0.2 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.1...3-0-maintenance) +### 3.0.2 / 2014-06-19 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.1...v3.0.2) Bug Fixes: From a11c1adec63cb2145dd0d01a616fa5af2b971271 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 20 Jun 2014 08:35:12 -0700 Subject: [PATCH 0663/1179] Address ruby warning. rspec-mocks/spec/rspec/mocks/and_yield_spec.rb:114: warning: assigned but unused variable - default_arg --- spec/rspec/mocks/and_yield_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index 0a24fa06e..ac746c51b 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -112,6 +112,8 @@ it "yields given argument when the argument is given" do default_arg = Object.new + allow(default_arg).to receive(:bar) + given_arg = Object.new obj = Object.new From fe35326eb17b367f61e481b02859aac1f575bf13 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 20 Jun 2014 08:46:48 -0700 Subject: [PATCH 0664/1179] Add failing spec for and_yield regression. --- spec/rspec/mocks/and_yield_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index ac746c51b..0e7200928 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -101,6 +101,7 @@ context "that are optional", :if => RSpec::Support::RubyFeatures.optional_and_splat_args_supported? do it "yields the default argument when the argument is not given" do + pending "Not sure how to achieve this yet. See rspec/rspec-mocks#714 and rspec/rspec-support#85." default_arg = Object.new obj = Object.new @@ -124,6 +125,17 @@ end end + it 'can yield to a block that uses `super`' do + klass = Class.new { def foo; 13; end } + subklass = Class.new(klass) { def foo(arg); arg.bar { super() }; end } + + arg = double + expect(arg).to receive(:bar).and_yield + + instance = subklass.new + instance.foo(arg) + end + it "fails when expectations set on the eval context and yielded arguments are not met" do configured_eval_context = nil yielded_arg = Object.new From e96c8c5a2be22d371f9dcf4c8668b8349c91dea7 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Wed, 25 Jun 2014 19:25:54 -0700 Subject: [PATCH 0665/1179] `have_received` matcher will raise "does not implement" errors correctly when used with verifying doubles and partial doubles. --- Changelog.md | 2 ++ lib/rspec/mocks/matchers/have_received.rb | 3 +++ lib/rspec/mocks/proxy.rb | 5 +++++ spec/rspec/mocks/partial_double_spec.rb | 7 +++++++ spec/rspec/mocks/spy_spec.rb | 12 ++++++++++++ 5 files changed, 29 insertions(+) diff --git a/Changelog.md b/Changelog.md index 489ed7ef4..7554076f1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,8 @@ Enhancements: * Add spying methods (`spy`, `ìnstance_spy`, `class_spy` and `object_spy`) which create doubles as null objects for use with spying in testing. (Sam Phippen, #671) +* `have_received` matcher will raise "does not implement" errors correctly when + used with verifying doubles and partial doubles. (Xavier Shay, #722) ### 3.0.2 / 2014-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.1...v3.0.2) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 245d08917..2675b54a9 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -22,6 +22,8 @@ def matches?(subject, &block) @block ||= block @subject = subject @expectation = expect + mock_proxy.ensure_implemented(@method_name) + expected_messages_received_in_order? end @@ -29,6 +31,7 @@ def does_not_match?(subject) @subject = subject ensure_count_unconstrained @expectation = expect.never + mock_proxy.ensure_implemented(@method_name) expected_messages_received_in_order? end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 2219b88e1..11074f580 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -8,6 +8,11 @@ def ==(expectation) end end + # @private + def ensure_implemented(*args) + # noop for basic proxies, see VerifyingProxy for behaviour. + end + # @private def initialize(object, order_group, name=nil, options={}) @object = object diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index d6cccbc00..76ef457a2 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -313,6 +313,13 @@ def defined_private_method prevents { expect(object).to receive(:unimplemented) } end + it 'boes not allow a spy on unimplemented method' do + allow(object).to receive(:object_id) + prevents(/does not implement/) { + expect(object).to have_received(:unimplemented) + } + end + it 'verifies arity range when matching arguments' do prevents { expect(object).to receive(:implemented).with('bogus') } end diff --git a/spec/rspec/mocks/spy_spec.rb b/spec/rspec/mocks/spy_spec.rb index 02650f319..08babd310 100644 --- a/spec/rspec/mocks/spy_spec.rb +++ b/spec/rspec/mocks/spy_spec.rb @@ -35,6 +35,18 @@ it "records called methods" do expect(subject.tap { |s| s.foo}).to have_received(:foo) end + + it 'fails fast when `have_received` is passed an undefined method name' do + expect { + expect(subject).to have_received(:bar) + }.to fail_matching("does not implement") + end + + it 'fails fast when negative `have_received` is passed an undefined method name' do + expect { + expect(subject).to_not have_received(:bar) + }.to fail_matching("does not implement") + end end describe "instance_spy" do From f55d6fa59e80606848be6282dcc731c77154c162 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Fri, 27 Jun 2014 08:18:59 -0700 Subject: [PATCH 0666/1179] Remove redundant allow. I originally thought this was required to allow use of the `have_received` matcher, but not sure what I was thinking. --- spec/rspec/mocks/partial_double_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 76ef457a2..2b5594ea4 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -313,8 +313,7 @@ def defined_private_method prevents { expect(object).to receive(:unimplemented) } end - it 'boes not allow a spy on unimplemented method' do - allow(object).to receive(:object_id) + it 'does not allow a spy on unimplemented method' do prevents(/does not implement/) { expect(object).to have_received(:unimplemented) } From b7c816914f39fd205a3b2b08c898a11982b41cea Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 7 Jul 2014 08:20:27 -0700 Subject: [PATCH 0667/1179] Preserve null objectness when copying test doubles. Fixes #732. --- Changelog.md | 7 +++++++ lib/rspec/mocks/test_double.rb | 5 +++++ spec/rspec/mocks/test_double_spec.rb | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/Changelog.md b/Changelog.md index 7554076f1..80a464131 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,13 @@ Enhancements: * `have_received` matcher will raise "does not implement" errors correctly when used with verifying doubles and partial doubles. (Xavier Shay, #722) +### 3.0.3 Development + +Bug Fixes: + +* Make `double.as_null_object.dup` and `double.as_null_object.clone` + make the copies be null objects. (Myron Marston, #732) + ### 3.0.2 / 2014-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.1...v3.0.2) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 74bfeffa8..5ff3f6ae1 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -119,6 +119,11 @@ def __raise_expired_error return false unless @__expired ErrorGenerator.new(self, @name).raise_expired_test_double_error end + + def initialize_copy(other) + as_null_object if other.null_object? + super + end end # A generic test double object. `double`, `instance_double` and friends diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 1509df84b..6aff70f07 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -25,6 +25,17 @@ module Mocks end end + RSpec.shared_examples_for "a copy method" do |method| + it "copies the `as_null_object` state when #{method}'d" do + dbl = double.as_null_object + copy = dbl.__send__(method) + expect(copy.foo.bar).to be(copy) + end + end + + include_examples "a copy method", :dup + include_examples "a copy method", :clone + [[:should, :expect], [:expect], [:should]].each do |syntax| context "with syntax #{syntax.inspect}" do include_context "with syntax", syntax From 4f857b3f576e69744cc4a3528b7e4b5427168fea Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 11 Jul 2014 09:45:03 +1000 Subject: [PATCH 0668/1179] attempt to reword docs --- features/verifying_doubles/dynamic_classes.feature | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index 3f46f3bec..78a754a71 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -2,12 +2,16 @@ Feature: Dynamic classes Verifying instance doubles do not support methods which the class reports to not exist since an actual instance of the class would be required to verify against. This is commonly - the case when `method_missing` is used. `ActiveRecord` does this to define methods from - database columns. If the object has already been loaded you may consider using an - [`object_double`](./using-an-object-double), but that cannot work if you are testing in isolation. + the case when `method_missing` is used. For example, `ActiveRecord` does this to define + methods from database columns. - These types of methods are supported at class level, since `respond_to?` can be queried - directly on the class. + There are a few ways to work around this. If the object has already been loaded you may + consider using an [`object_double`](./using-an-object-double), but that cannot work if you + are testing in isolation. Alternatively you could implement the methods directly (calling + `super` to return the `method_missing` definition). + + These types of methods are supported at class level (with `class_double`) however, since + `respond_to?` can be queried directly on the class. Background: Given a file named "lib/fake_active_record.rb" with: From 81b35d6e7fa88c183e85adc918def3417008bfd6 Mon Sep 17 00:00:00 2001 From: Chris Griego Date: Sat, 12 Jul 2014 01:11:49 -0500 Subject: [PATCH 0669/1179] Don't inadvertently define BasicObject --- lib/rspec/mocks/syntax.rb | 184 ++++++++++++++++---------------- spec/rspec/mocks/syntax_spec.rb | 7 ++ 2 files changed, 100 insertions(+), 91 deletions(-) create mode 100644 spec/rspec/mocks/syntax_spec.rb diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index ebd20994e..1b5af1211 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -202,97 +202,99 @@ def self.default_should_syntax_host end end -# The legacy `:should` syntax adds the following methods directly to -# `BasicObject` so that they are available off of any object. Note, however, -# that this syntax does not always play nice with delegate/proxy objects. -# We recommend you use the non-monkeypatching `:expect` syntax instead. -# @see Class -class BasicObject - # @method should_receive - # Sets an expectation that this object should receive a message before - # the end of the example. - # - # @example - # - # logger = double('logger') - # thing_that_logs = ThingThatLogs.new(logger) - # logger.should_receive(:log) - # thing_that_logs.do_something_that_logs_a_message - # - # @note This is only available when you have enabled the `should` syntax. - # @see RSpec::Mocks::ExampleMethods#expect - - # @method should_not_receive - # Sets and expectation that this object should _not_ receive a message - # during this example. - # @see RSpec::Mocks::ExampleMethods#expect - - # @method stub - # Tells the object to respond to the message with the specified value. - # - # @example - # - # counter.stub(:count).and_return(37) - # counter.stub(:count => 37) - # counter.stub(:count) { 37 } - # - # @note This is only available when you have enabled the `should` syntax. - # @see RSpec::Mocks::ExampleMethods#allow - - # @method unstub - # Removes a stub. On a double, the object will no longer respond to - # `message`. On a real object, the original method (if it exists) is - # restored. - # - # This is rarely used, but can be useful when a stub is set up during a - # shared `before` hook for the common case, but you want to replace it - # for a special case. - # - # @note This is only available when you have enabled the `should` syntax. - - # @method stub_chain - # @overload stub_chain(method1, method2) - # @overload stub_chain("method1.method2") - # @overload stub_chain(method1, method_to_value_hash) - # - # Stubs a chain of methods. - # - # ## Warning: - # - # Chains can be arbitrarily long, which makes it quite painless to - # violate the Law of Demeter in violent ways, so you should consider any - # use of `stub_chain` a code smell. Even though not all code smells - # indicate real problems (think fluent interfaces), `stub_chain` still - # results in brittle examples. For example, if you write - # `foo.stub_chain(:bar, :baz => 37)` in a spec and then the - # implementation calls `foo.baz.bar`, the stub will not work. - # - # @example - # - # double.stub_chain("foo.bar") { :baz } - # double.stub_chain(:foo, :bar => :baz) - # double.stub_chain(:foo, :bar) { :baz } - # - # # Given any of ^^ these three forms ^^: - # double.foo.bar # => :baz - # - # # Common use in Rails/ActiveRecord: - # Article.stub_chain("recent.published") { [Article.new] } - # - # @note This is only available when you have enabled the `should` syntax. - # @see RSpec::Mocks::ExampleMethods#receive_message_chain - - # @method as_null_object - # Tells the object to respond to all messages. If specific stub values - # are declared, they'll work as expected. If not, the receiver is - # returned. - # - # @note This is only available when you have enabled the `should` syntax. - - # @method null_object? - # Returns true if this object has received `as_null_object` - # - # @note This is only available when you have enabled the `should` syntax. +if defined?(BasicObject) + # The legacy `:should` syntax adds the following methods directly to + # `BasicObject` so that they are available off of any object. Note, however, + # that this syntax does not always play nice with delegate/proxy objects. + # We recommend you use the non-monkeypatching `:expect` syntax instead. + # @see Class + class BasicObject + # @method should_receive + # Sets an expectation that this object should receive a message before + # the end of the example. + # + # @example + # + # logger = double('logger') + # thing_that_logs = ThingThatLogs.new(logger) + # logger.should_receive(:log) + # thing_that_logs.do_something_that_logs_a_message + # + # @note This is only available when you have enabled the `should` syntax. + # @see RSpec::Mocks::ExampleMethods#expect + + # @method should_not_receive + # Sets and expectation that this object should _not_ receive a message + # during this example. + # @see RSpec::Mocks::ExampleMethods#expect + + # @method stub + # Tells the object to respond to the message with the specified value. + # + # @example + # + # counter.stub(:count).and_return(37) + # counter.stub(:count => 37) + # counter.stub(:count) { 37 } + # + # @note This is only available when you have enabled the `should` syntax. + # @see RSpec::Mocks::ExampleMethods#allow + + # @method unstub + # Removes a stub. On a double, the object will no longer respond to + # `message`. On a real object, the original method (if it exists) is + # restored. + # + # This is rarely used, but can be useful when a stub is set up during a + # shared `before` hook for the common case, but you want to replace it + # for a special case. + # + # @note This is only available when you have enabled the `should` syntax. + + # @method stub_chain + # @overload stub_chain(method1, method2) + # @overload stub_chain("method1.method2") + # @overload stub_chain(method1, method_to_value_hash) + # + # Stubs a chain of methods. + # + # ## Warning: + # + # Chains can be arbitrarily long, which makes it quite painless to + # violate the Law of Demeter in violent ways, so you should consider any + # use of `stub_chain` a code smell. Even though not all code smells + # indicate real problems (think fluent interfaces), `stub_chain` still + # results in brittle examples. For example, if you write + # `foo.stub_chain(:bar, :baz => 37)` in a spec and then the + # implementation calls `foo.baz.bar`, the stub will not work. + # + # @example + # + # double.stub_chain("foo.bar") { :baz } + # double.stub_chain(:foo, :bar => :baz) + # double.stub_chain(:foo, :bar) { :baz } + # + # # Given any of ^^ these three forms ^^: + # double.foo.bar # => :baz + # + # # Common use in Rails/ActiveRecord: + # Article.stub_chain("recent.published") { [Article.new] } + # + # @note This is only available when you have enabled the `should` syntax. + # @see RSpec::Mocks::ExampleMethods#receive_message_chain + + # @method as_null_object + # Tells the object to respond to all messages. If specific stub values + # are declared, they'll work as expected. If not, the receiver is + # returned. + # + # @note This is only available when you have enabled the `should` syntax. + + # @method null_object? + # Returns true if this object has received `as_null_object` + # + # @note This is only available when you have enabled the `should` syntax. + end end # The legacy `:should` syntax adds the `any_instance` to `Class`. diff --git a/spec/rspec/mocks/syntax_spec.rb b/spec/rspec/mocks/syntax_spec.rb new file mode 100644 index 000000000..ad84af834 --- /dev/null +++ b/spec/rspec/mocks/syntax_spec.rb @@ -0,0 +1,7 @@ +module RSpec + RSpec.describe Mocks do + it "does not inadvertently define BasicObject on 1.8", :if => RUBY_VERSION.to_f < 1.9 do + expect(defined?(::BasicObject)).to be nil + end + end +end From 873d755efe61d500444c230dc3b7f4f9d24aed43 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 13 Jul 2014 00:01:01 +1000 Subject: [PATCH 0670/1179] changelog for #739 --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 80a464131..b0006e19e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Enhancements: Phippen, #671) * `have_received` matcher will raise "does not implement" errors correctly when used with verifying doubles and partial doubles. (Xavier Shay, #722) +* Don't inadvertently define `BasicObject` in 1.8.7. (Chris Griego, #739) ### 3.0.3 Development From 0d2f5992d28e95a12307c3ac2c8a6e16cbc2ca3e Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 13 Jul 2014 15:22:38 -0700 Subject: [PATCH 0671/1179] Allow matchers to be used in place of keyword arguments in `with` expectations. --- Changelog.md | 2 + lib/rspec/mocks/argument_matchers.rb | 19 ++++++++ lib/rspec/mocks/message_expectation.rb | 2 +- .../mocks/verifying_message_expecation.rb | 10 ++-- lib/rspec/mocks/verifying_proxy.rb | 2 +- spec/rspec/mocks/verifying_double_spec.rb | 47 ++++++++++++++++++- .../verifying_message_expecation_spec.rb | 4 +- 7 files changed, 77 insertions(+), 9 deletions(-) diff --git a/Changelog.md b/Changelog.md index b0006e19e..81a74b738 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,8 @@ Enhancements: * `have_received` matcher will raise "does not implement" errors correctly when used with verifying doubles and partial doubles. (Xavier Shay, #722) * Don't inadvertently define `BasicObject` in 1.8.7. (Chris Griego, #739) +* Allow matchers to be used in place of keyword arguments in `with` + expectations. (Xavier Shay, #726) ### 3.0.3 Development diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index dbaf60df2..1e3dc6567 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -1,3 +1,8 @@ +# This cannot take advantage of our relative requires, since this file is a +# dependency of `rspec/mocks/argument_list_matcher.rb`. See comment there for +# details. +require 'rspec/support/matcher_definition' + module RSpec module Mocks @@ -244,6 +249,20 @@ def description end end + matcher_namespace = name + '::' + ::RSpec::Support.register_matcher_definition do |object| + # This is the best we have for now. We should tag all of our matchers + # with a module or something so we can test for it directly. + # + # (Note Module#parent in ActiveSupport is defined in a similar way.) + begin + object.class.name.include?(matcher_namespace) + rescue NoMethodError + # Some objects, like BasicObject, don't implemented standard + # reflection methods. + false + end + end end end end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 7c7ce6494..d36e33887 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -555,7 +555,7 @@ def call(*args_to_ignore, &block) block_signature = Support::BlockSignature.new(block) @args_to_yield.each do |args| - unless Support::MethodSignatureVerifier.new(block_signature, args).valid? + unless Support::StrictSignatureVerifier.new(block_signature, args).valid? @error_generator.raise_wrong_arity_error(args, block_signature) end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 93df99d10..ea20e3048 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -35,18 +35,22 @@ def with(*args, &block) super end - validate_arguments!(expected_args) + validate_expected_arguments!(expected_args) end super end private - def validate_arguments!(actual_args) + def validate_expected_arguments!(actual_args) return if method_reference.nil? method_reference.with_signature do |signature| - verifier = Support::MethodSignatureVerifier.new(signature, actual_args) + verifier = Support::LooseSignatureVerifier.new( + signature, + actual_args + ) + unless verifier.valid? # Fail fast is required, otherwise the message expecation will fail # as well ("expected method not called") and clobber this one. diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index f4cd3ab70..b9349cf9b 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -132,7 +132,7 @@ def proxy_method_invoked(obj, *args, &block) def validate_arguments!(actual_args) @method_reference.with_signature do |signature| - verifier = Support::MethodSignatureVerifier.new(signature, actual_args) + verifier = Support::StrictSignatureVerifier.new(signature, actual_args) unless verifier.valid? raise ArgumentError, verifier.error_message end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index a91863d35..a9a88a2d5 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -37,7 +37,10 @@ def defined_instance_method if required_kw_args_supported? # Need to eval this since it is invalid syntax on earlier rubies. eval <<-RUBY - def kw_args_method(optional_arg:'hello', required_arg:) + def kw_args_method(foo, optional_arg:'hello', required_arg:) + end + + def mixed_args_method(foo, bar, optional_arg_1:1, optional_arg_2:2) end RUBY end @@ -243,7 +246,47 @@ module Mocks if required_kw_args_supported? it 'allows keyword arguments' do o = instance_double('LoadedClass', :kw_args_method => true) - expect(o.kw_args_method(:required_arg => 'something')).to eq(true) + expect(o.kw_args_method(1, :required_arg => 'something')).to eq(true) + end + + context 'for a method that only accepts keyword args' do + it 'allows hash matchers like `hash_including` to be used in place of the keywords arg hash' do + o = instance_double('LoadedClass') + expect(o).to receive(:kw_args_method). + with(1, hash_including(:required_arg => 1)) + o.kw_args_method(1, :required_arg => 1) + end + + it 'allows anything matcher to be used in place of the keywords arg hash' do + o = instance_double('LoadedClass') + expect(o).to receive(:kw_args_method).with(1, anything) + o.kw_args_method(1, :required_arg => 1) + end + + it 'still checks positional arguments when matchers used for keyword args' do + o = instance_double('LoadedClass') + prevents(/Expected 1, got 3/) { + expect(o).to receive(:kw_args_method). + with(1, 2, 3, hash_including(:required_arg => 1)) + } + end + + it 'does not allow matchers to be used in an actual method call' do + o = instance_double('LoadedClass') + matcher = hash_including(:required_arg => 1) + allow(o).to receive(:kw_args_method).with(1, matcher) + expect { + o.kw_args_method(1, matcher) + }.to raise_error(ArgumentError) + end + end + + context 'for a method that accepts a mix of optional keyword and positional args' do + it 'allows hash matchers like `hash_including` to be used in place of the keywords arg hash' do + o = instance_double('LoadedClass') + expect(o).to receive(:mixed_args_method).with(1, 2, hash_including(:optional_arg_1 => 1)) + o.mixed_args_method(1, 2, :optional_arg_1 => 1) + end end it 'checks that stubbed methods with required keyword args are ' + diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 9d1fea555..66e0b60c4 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -22,7 +22,7 @@ module Mocks args = ["abc123", "xyz987"] subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_invalid_arguments_error). - with(instance_of(Support::MethodSignatureVerifier)) + with(instance_of(Support::LooseSignatureVerifier)) subject.with(*args) end @@ -48,7 +48,7 @@ module Mocks it 'matches arity to 0' do subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) expect(error_generator).to receive(:raise_invalid_arguments_error). - with(instance_of(Support::MethodSignatureVerifier)) + with(instance_of(Support::LooseSignatureVerifier)) subject.with(no_args) end From 45041ca6ad8a9a7882e08bd4de61628838e6172e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 21 Jul 2014 16:12:11 -0700 Subject: [PATCH 0672/1179] Forwardport 2.99.2 release notes. [ci skip] --- Changelog.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index b0006e19e..2c920d116 100644 --- a/Changelog.md +++ b/Changelog.md @@ -183,7 +183,7 @@ Bug Fixes: behavior. (Maurício Linhares) ### 3.0.0.beta1 / 2013-11-07 -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0...v3.0.0.beta1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.2...v3.0.0.beta1) Breaking Changes for 3.0.0: @@ -246,6 +246,14 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.2 / 2014-07-21 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.1...v2.99.2) + +Enhancements: + +* Warn about upcoming change to `#===` matching and `DateTime#===` behaviour. + (Jon Rowe, #735) + ### 2.99.1 / 2014-06-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0...v2.99.1) From c2aa35c21a4c73070f8cf4176343cafc644cfae5 Mon Sep 17 00:00:00 2001 From: Karthik T Date: Tue, 29 Jul 2014 17:13:12 +0800 Subject: [PATCH 0673/1179] Add undocumented argument matchers to README Add hash_including and array_including to README Discovered via http://stackoverflow.com/a/25011672/1520364 --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b845f786..878a13eb4 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,11 @@ expect(double).to receive(:msg).with(1, boolean(), "b") #2nd argument can be tru expect(double).to receive(:msg).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp expect(double).to receive(:msg).with(1, anything(), "b") #2nd argument can be anything at all expect(double).to receive(:msg).with(1, duck_type(:abs, :div), "b") - #2nd argument can be object that responds to #abs and #div + #2nd argument can be object that responds to #abs and #div +expect(double).to receive(:msg).with(hash_including(a: 5)) # first arg is a hash with a: 5 as one of the key-values +expect(double).to receive(:msg).with(array_including(5)) # first arg is an array with 5 as one of the key-values +expect(double).to receive(:msg).with(hash_not_including(a:5) # first arg is a hash without a: 5 as one of the key-values +expect(double).to receive(:msg).with(hash_excluding(a:5)) # first arg is a hash without a: 5 as one of the key-values ``` ## Receive Counts From 654b34a36ceefa3b12de967a2c9416ff25a8d1fd Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 31 Jul 2014 15:19:28 +1000 Subject: [PATCH 0674/1179] cleanup readme post #742 --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 878a13eb4..4765b9e28 100644 --- a/README.md +++ b/README.md @@ -209,12 +209,10 @@ expect(double).to receive(:msg).with(1, kind_of(Numeric), "b") #2nd argument can expect(double).to receive(:msg).with(1, boolean(), "b") #2nd argument can be true or false expect(double).to receive(:msg).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp expect(double).to receive(:msg).with(1, anything(), "b") #2nd argument can be anything at all -expect(double).to receive(:msg).with(1, duck_type(:abs, :div), "b") - #2nd argument can be object that responds to #abs and #div -expect(double).to receive(:msg).with(hash_including(a: 5)) # first arg is a hash with a: 5 as one of the key-values +expect(double).to receive(:msg).with(1, duck_type(:abs, :div), "b") #2nd argument can be object that responds to #abs and #div +expect(double).to receive(:msg).with(hash_including(:a => 5)) # first arg is a hash with a: 5 as one of the key-values expect(double).to receive(:msg).with(array_including(5)) # first arg is an array with 5 as one of the key-values -expect(double).to receive(:msg).with(hash_not_including(a:5) # first arg is a hash without a: 5 as one of the key-values -expect(double).to receive(:msg).with(hash_excluding(a:5)) # first arg is a hash without a: 5 as one of the key-values +expect(double).to receive(:msg).with(hash_excluding(:a => 5)) # first arg is a hash without a: 5 as one of the key-values ``` ## Receive Counts From 845a033fe810fe907e9b8b0ccfb805519b344581 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 1 Aug 2014 00:25:42 -0700 Subject: [PATCH 0675/1179] Prevent load time warnings. --- spec/rspec/mocks_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 1d095828d..3650b9a6c 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -1,4 +1,13 @@ +require 'rspec/support/spec/prevent_load_time_warnings' + describe RSpec::Mocks do + it_behaves_like 'a library that issues no warnings when loaded', + 'rspec-mocks', 'require "rspec/mocks"', + # Must be required before other files due to how our autoloads are setup. + # (Users won't hit this problem because they won't require all the files + # individually in whatever order the file system returns) + 'require "rspec/mocks/any_instance"' + describe ".verify" do it "delegates to the space" do foo = double From a97ac69b7785ebc6275140741a51f5e082189b36 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Aug 2014 14:01:08 +1000 Subject: [PATCH 0676/1179] restore kind_of to match using kind_of? rather than === --- lib/rspec/mocks/argument_matchers.rb | 17 ++++++++++++++++- spec/rspec/mocks/argument_matchers_spec.rb | 13 +++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 1e3dc6567..65f4905db 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -115,7 +115,7 @@ def instance_of(klass) # # expect(object).to receive(:message).with(kind_of(Thing)) def kind_of(klass) - klass + KindOf.new(klass) end alias_method :a_kind_of, :kind_of @@ -249,6 +249,21 @@ def description end end + # @private + class KindOf + def initialize(klass) + @klass = klass + end + + def ===(actual) + actual.kind_of?(@klass) + end + + def description + @klass.name + end + end + matcher_namespace = name + '::' ::RSpec::Support.register_matcher_definition do |object| # This is the best we have for now. We should tag all of our matchers diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index 75448de0f..f9cd1c21b 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -43,6 +43,19 @@ module Mocks a_double.random_call(true) }.to fail_matching "expected: (Numeric)" end + + it "matches arguments that have defined `kind_of?` to return true" do + fix_num = double(:kind_of? => true) + expect(a_double).to receive(:random_call).with(kind_of(Numeric)) + a_double.random_call(fix_num) + end + + it "handles a class thats overridden ===" do + allow(Numeric).to receive(:===) { false } + fix_num = double(:kind_of? => true) + expect(a_double).to receive(:random_call).with(kind_of(Numeric)) + a_double.random_call(fix_num) + end end describe "instance_of" do From f579ad80264732890565c27c4ea28847f7ca2a28 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Aug 2014 17:31:10 +1000 Subject: [PATCH 0677/1179] tweak kind_of message --- lib/rspec/mocks/argument_matchers.rb | 2 +- spec/rspec/mocks/argument_matchers_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 65f4905db..f5916c59f 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -260,7 +260,7 @@ def ===(actual) end def description - @klass.name + "kind of #{@klass.name}" end end diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index f9cd1c21b..8c2c755dd 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -41,7 +41,7 @@ module Mocks expect(a_double).to receive(:random_call).with(kind_of(Numeric)) expect { a_double.random_call(true) - }.to fail_matching "expected: (Numeric)" + }.to fail_matching "expected: (kind of Numeric)" end it "matches arguments that have defined `kind_of?` to return true" do From adbb691fbdee21c83c5337b2da576fc40b393e2e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Aug 2014 14:05:19 +1000 Subject: [PATCH 0678/1179] changelog for #750 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 285801395..78fa857a2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,8 @@ Bug Fixes: * Make `double.as_null_object.dup` and `double.as_null_object.clone` make the copies be null objects. (Myron Marston, #732) +* Restore `kind_of(x)` to match using `arg.kind_of?` (like RSpec 2) + rather than `x === arg`. (Jon Rowe, #750). ### 3.0.2 / 2014-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.1...v3.0.2) From d7052ed8d0feaae08d9a00d880d68189c1bd4d46 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 5 Aug 2014 08:04:37 +1000 Subject: [PATCH 0679/1179] fixup changelog post 3.0.3 release [skip ci] --- Changelog.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 78fa857a2..203c8e684 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.1.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.2...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...master) Enhancements: @@ -12,14 +12,23 @@ Enhancements: * Allow matchers to be used in place of keyword arguments in `with` expectations. (Xavier Shay, #726) -### 3.0.3 Development +### 3.0.4 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3-0-maintenance) + +Bug Fixes: +* Restore `kind_of(x)` to match using `arg.kind_of?` (like RSpec 2) + rather than `x === arg`. (Jon Rowe, #750). + +### 3.0.3 / 2014-07-21 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.2...v3.0.3) Bug Fixes: +* `have_received` matcher will raise "does not implement" errors correctly when + used with verifying doubles and partial doubles. (Xavier Shay, #722) * Make `double.as_null_object.dup` and `double.as_null_object.clone` make the copies be null objects. (Myron Marston, #732) -* Restore `kind_of(x)` to match using `arg.kind_of?` (like RSpec 2) - rather than `x === arg`. (Jon Rowe, #750). +* Don't inadvertently define `BasicObject` in 1.8.7. (Chris Griego, #739) ### 3.0.2 / 2014-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.1...v3.0.2) From 8a2ec5f3f8d0fb2a2e8ca9c88ece2d0288aff83b Mon Sep 17 00:00:00 2001 From: Dennis Taylor Date: Wed, 6 Aug 2014 10:59:12 -0700 Subject: [PATCH 0680/1179] Added 'thrice' expectation. See https://github.com/rspec/rspec-expectations/pull/615 --- lib/rspec/mocks/message_expectation.rb | 11 ++++ spec/rspec/mocks/thrice_counts_spec.rb | 72 ++++++++++++++++++++++++++ spec/rspec/mocks/twice_counts_spec.rb | 2 +- 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 spec/rspec/mocks/thrice_counts_spec.rb diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 7c7ce6494..c401121f3 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -430,6 +430,17 @@ def twice(&block) self end + # Expect a message to be received exactly three times. + # + # @example + # + # expect(car).to receive(:go).thrice + def thrice(&block) + self.inner_implementation_action = block + set_expected_received_count :exactly, 3 + self + end + # Expect messages to be received in a specific order. # # @example diff --git a/spec/rspec/mocks/thrice_counts_spec.rb b/spec/rspec/mocks/thrice_counts_spec.rb new file mode 100644 index 000000000..00147e43b --- /dev/null +++ b/spec/rspec/mocks/thrice_counts_spec.rb @@ -0,0 +1,72 @@ +module RSpec + module Mocks + describe "#thrice" do + before(:each) do + @double = double("test double") + end + + it "passes when called thrice" do + expect(@double).to receive(:do_something).thrice + 3.times { @double.do_something } + verify @double + end + + it "passes when called thrice with specified args" do + expect(@double).to receive(:do_something).thrice.with("1", 1) + 3.times { @double.do_something("1", 1) } + verify @double + end + + it "passes when called thrice with unspecified args" do + expect(@double).to receive(:do_something).thrice + @double.do_something("1") + @double.do_something(1) + @double.do_something(nil) + verify @double + end + + it "fails fast when call count is higher than expected" do + expect(@double).to receive(:do_something).thrice + 3.times { @double.do_something } + expect { + @double.do_something + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "fails when call count is lower than expected" do + expect(@double).to receive(:do_something).thrice + @double.do_something + expect { + verify @double + }.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "fails when called with wrong args on the first call" do + expect(@double).to receive(:do_something).thrice.with("1", 1) + expect { + @double.do_something(1, "1") + }.to raise_error(RSpec::Mocks::MockExpectationError) + reset @double + end + + it "fails when called with wrong args on the second call" do + expect(@double).to receive(:do_something).thrice.with("1", 1) + @double.do_something("1", 1) + expect { + @double.do_something(1, "1") + }.to raise_error(RSpec::Mocks::MockExpectationError) + reset @double + end + + it "fails when called with wrong args on the third call" do + expect(@double).to receive(:do_something).thrice.with("1", 1) + @double.do_something("1", 1) + @double.do_something("1", 1) + expect { + @double.do_something(1, "1") + }.to raise_error(RSpec::Mocks::MockExpectationError) + reset @double + end + end + end +end diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index ed454045f..e6c2ebeae 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -43,7 +43,7 @@ module Mocks }.to raise_error(RSpec::Mocks::MockExpectationError) end - it "fails when called wrong args on the first call" do + it "fails when called with wrong args on the first call" do expect(@double).to receive(:do_something).twice.with("1", 1) expect { @double.do_something(1, "1") From 33a7f1f612e5630217214674b275415168da0fec Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 7 Aug 2014 13:45:09 +1000 Subject: [PATCH 0681/1179] changelog for #753 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 203c8e684..ea32222c0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,8 @@ Enhancements: * Don't inadvertently define `BasicObject` in 1.8.7. (Chris Griego, #739) * Allow matchers to be used in place of keyword arguments in `with` expectations. (Xavier Shay, #726) +* Include `thrice` matcher as a synonym for `exactly(3).times`. + (Dennis Taylor, #753) ### 3.0.4 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3-0-maintenance) From 2a60b8d4fae27c67ed963d137751295676a720d5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 8 Aug 2014 18:57:42 -0700 Subject: [PATCH 0682/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 123 +++++++++++++++++++++++++++++++++++ .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 5 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 .rubocop_rspec_base.yml diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml new file mode 100644 index 000000000..25e4703a6 --- /dev/null +++ b/.rubocop_rspec_base.yml @@ -0,0 +1,123 @@ +# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +# This file contains defaults for RSpec projects. Individual projects +# can customize by inheriting this file and overriding particular settings. + +AccessModifierIndentation: + EnforcedStyle: outdent + +# "Use alias_method instead of alias" +# We're fine with `alias`. +Alias: + Enabled: false + +AlignParameters: + EnforcedStyle: with_first_parameter + +# "Avoid the use of the case equality operator ===" +# We prefer using `Class#===` over `Object#is_a?` because `Class#===` +# is less likely to be monkey patched than `is_a?` on a user object. +CaseEquality: + Enabled: false + +# Warns when the class is excessively long. +ClassLength: + Max: 100 + +CollectionMethods: + PreferredMethods: + reduce: 'inject' + +# Over time we'd like to get this down, but this is what we're at now. +CyclomaticComplexity: + Max: 10 + +# We use YARD to enforce documentation. It works better than rubocop's +# enforcement...rubocop complains about the places we re-open +# `RSpec::Expectations` and `RSpec::Matchers` w/o having doc commments. +Documentation: + Enabled: false + +# We still support 1.8.7 which requires trailing dots +DotPosition: + EnforcedStyle: trailing + +DoubleNegation: + Enabled: false + +# each_with_object is unavailable on 1.8.7 so we have to disable this one. +EachWithObject: + Enabled: false + +Encoding: + EnforcedStyle: when_needed + +FormatString: + EnforcedStyle: percent + +# As long as we support ruby 1.8.7 we have to use hash rockets. +HashSyntax: + EnforcedStyle: hash_rockets + +# We can't use the new lambda syntax, since we still support 1.8.7. +Lambda: + Enabled: false + +# Over time we'd like to get this down, but this is what we're at now. +LineLength: + Max: 100 + +# Over time we'd like to get this down, but this is what we're at now. +MethodLength: + Max: 15 + +# Who cares what we call the argument for binary operator methods? +OpMethod: + Enabled: false + +PercentLiteralDelimiters: + PreferredDelimiters: + '%': () # double-quoted string + '%i': '[]' # array of symbols + '%q': () # single-quoted string + '%Q': () # double-quoted string + '%r': '{}' # regular expression pattern + '%s': () # a symbol + '%w': '[]' # array of single-quoted strings + '%W': '[]' # array of double-quoted strings + '%x': () # a shell command as a string + +# We have too many special cases where we allow generator methods or prefer a +# prefixed predicate due to it's improved readability. +PredicateName: + Enabled: false + +# On 1.8 `proc` is `lambda`, so we use `Proc.new` to ensure we get real procs on all supported versions. +# http://batsov.com/articles/2014/02/04/the-elements-of-style-in-ruby-number-12-proc-vs-proc-dot-new/ +Proc: + Enabled: false + +RedundantReturn: + AllowMultipleReturnValues: true + +# We have to rescue Exception in the `raise_error` matcher for it to work properly. +RescueException: + Enabled: false + +# We haven't adopted the `fail` to signal exceptions vs `raise` for re-raises convention. +SignalException: + Enabled: false + +# We've tended to use no space, so it's less of a change to stick with that. +SpaceAroundEqualsInParameterDefault: + EnforcedStyle: no_space + +# We don't care about single vs double qoutes. +StringLiterals: + Enabled: false + +TrivialAccessors: + AllowDSLWriters: true + AllowPredicates: true + ExactNameMatch: true diff --git a/.travis.yml b/.travis.yml index eccf74982..46b3690f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-06-06T11:34:33-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 2a6c29c2f..ab25cbd61 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-06-06T11:34:33-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index a533088af..c3616c2fa 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-06-06T11:34:33-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index 1bfb23670..af4c620d1 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-06-06T11:34:33-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From 79b1fc37ebc7e924e75bbf8fb2a5bf860101c319 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 12 Aug 2014 10:42:18 -0700 Subject: [PATCH 0683/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 6 +++--- script/run_build | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 25e4703a6..02c95440e 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 46b3690f8..35b70e134 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index ab25cbd61..e7e4e58bc 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index c3616c2fa..11ad99777 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html @@ -77,7 +77,7 @@ function style_and_lint_enforced { function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there - travis_retry git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH + travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" fi; } @@ -131,7 +131,7 @@ function run_spec_suite_for { echo unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - travis_retry bundle install $bundle_install_flags + travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done popd fi; diff --git a/script/run_build b/script/run_build index af4c620d1..b86c6ffca 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-08T18:57:18-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From 09a1b64869901be543d028a88f394c525c4f4b7b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 14 Aug 2014 10:09:28 -0700 Subject: [PATCH 0684/1179] Forwardport 3.0.4 release notes. [ci skip] --- Changelog.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index ea32222c0..3d35456f2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.1.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3.0.4) Enhancements: @@ -14,12 +14,13 @@ Enhancements: * Include `thrice` matcher as a synonym for `exactly(3).times`. (Dennis Taylor, #753) -### 3.0.4 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3-0-maintenance) +### 3.0.4 / 2014-08-14 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3.0.4) Bug Fixes: -* Restore `kind_of(x)` to match using `arg.kind_of?` (like RSpec 2) - rather than `x === arg`. (Jon Rowe, #750). + +* Restore `kind_of(x)` to match using `arg.kind_of?(x)` (like RSpec 2) + rather than `x === arg`. (Jon Rowe, #750) ### 3.0.3 / 2014-07-21 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.2...v3.0.3) From 1c947aff590712f83922343792d8ae3ddc63e8bc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 14 Aug 2014 10:20:21 -0700 Subject: [PATCH 0685/1179] Fixup changelog a bit. [ci skip] --- Changelog.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3d35456f2..81ca6a402 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,11 +8,10 @@ Enhancements: Phippen, #671) * `have_received` matcher will raise "does not implement" errors correctly when used with verifying doubles and partial doubles. (Xavier Shay, #722) -* Don't inadvertently define `BasicObject` in 1.8.7. (Chris Griego, #739) * Allow matchers to be used in place of keyword arguments in `with` expectations. (Xavier Shay, #726) -* Include `thrice` matcher as a synonym for `exactly(3).times`. - (Dennis Taylor, #753) +* Add `thrice` modifier to message expectation interface as a synonym + for `exactly(3).times`. (Dennis Taylor, #753) ### 3.0.4 / 2014-08-14 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3.0.4) From a008bff6995ded3b42cea08d90d7efb9b69fb1fb Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 7 Aug 2014 13:55:26 +1000 Subject: [PATCH 0686/1179] support thrice with at_least and at_most --- lib/rspec/mocks/message_expectation.rb | 1 + spec/rspec/mocks/at_least_spec.rb | 26 ++++++++++++++++++++++++++ spec/rspec/mocks/at_most_spec.rb | 25 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index b022c30fc..5628a16f8 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -524,6 +524,7 @@ def set_expected_received_count(relativity, n) when Numeric then n when :once then 1 when :twice then 2 + when :thrice then 3 end end diff --git a/spec/rspec/mocks/at_least_spec.rb b/spec/rspec/mocks/at_least_spec.rb index 12068d224..c8d394470 100644 --- a/spec/rspec/mocks/at_least_spec.rb +++ b/spec/rspec/mocks/at_least_spec.rb @@ -42,6 +42,15 @@ module Mocks }.to raise_error(/expected: at least 2 times.*received: 0 times/m) end + it "fails when at least thrice method is called less than three times" do + expect(@double).to receive(:do_something).at_least(:thrice) + @double.do_something + @double.do_something + expect { + verify @double + }.to raise_error(/expected: at least 3 times.*received: 2 times/m) + end + it "passes when at least n times method is called exactly n times" do expect(@double).to receive(:do_something).at_least(4).times @double.do_something @@ -89,6 +98,23 @@ module Mocks verify @double end + it "passes when at least thrice method is called three times" do + expect(@double).to receive(:do_something).at_least(:thrice) + @double.do_something + @double.do_something + @double.do_something + verify @double + end + + it "passes when at least thrice method is called four times" do + expect(@double).to receive(:do_something).at_least(:thrice) + @double.do_something + @double.do_something + @double.do_something + @double.do_something + verify @double + end + it "returns the value given by a block when the at least once method is called" do expect(@double).to receive(:to_s).at_least(:once) { "testing" } expect(@double.to_s).to eq "testing" diff --git a/spec/rspec/mocks/at_most_spec.rb b/spec/rspec/mocks/at_most_spec.rb index 956619a67..8b92a4a10 100644 --- a/spec/rspec/mocks/at_most_spec.rb +++ b/spec/rspec/mocks/at_most_spec.rb @@ -52,6 +52,20 @@ module Mocks verify @double end + it "passes when at_most(:thrice) is called less than three times" do + expect(@double).to receive(:do_something).at_most(:thrice) + @double.do_something + verify @double + end + + it "passes when at_most(:thrice) is called thrice" do + expect(@double).to receive(:do_something).at_most(:thrice) + @double.do_something + @double.do_something + @double.do_something + verify @double + end + it "returns the value given by a block when at_most(:once) method is called" do expect(@double).to receive(:to_s).at_most(:once) { "testing" } expect(@double.to_s).to eq "testing" @@ -83,6 +97,17 @@ module Mocks @double.do_something }.to raise_error(/expected: at most 2 times.*received: 3 times/m) end + + it "fails fast when at_most(:thrice) and is called four times" do + expect(@double).to receive(:do_something).at_most(:thrice) + @double.do_something + @double.do_something + @double.do_something + expect { + @double.do_something + }.to raise_error(/expected: at most 3 times.*received: 4 times/m) + end + end end end From 2830f89b70602d9d67a55da14c3d76d959df38ba Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 7 Aug 2014 14:00:36 +1000 Subject: [PATCH 0687/1179] support thrice for have_received --- lib/rspec/mocks/matchers/have_received.rb | 2 +- .../mocks/matchers/have_received_spec.rb | 47 ++++++++++++++++--- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 2675b54a9..10a836a27 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -3,7 +3,7 @@ module Mocks module Matchers # @private class HaveReceived - COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice) + COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice thrice) ARGS_CONSTRAINTS = %w(with) CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w(ordered) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 64d9be800..95e3d2cca 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -251,10 +251,43 @@ module Mocks }.to raise_error(/expected: 2 times.*received: 3 times/m) end end + + context 'thrice' do + it 'passes when the message was received thrice' do + dbl = double(:expected_method => nil) + dbl.expected_method + dbl.expected_method + dbl.expected_method + + expect(dbl).to have_received(:expected_method).thrice + end + + it 'fails when the message was received less than three times' do + dbl = double(:expected_method => nil) + dbl.expected_method + dbl.expected_method + + expect { + expect(dbl).to have_received(:expected_method).thrice + }.to raise_error(/expected: 3 times.*received: 2 times/m) + end + + it 'fails when the message was received more than times' do + dbl = double(:expected_method => nil) + dbl.expected_method + dbl.expected_method + dbl.expected_method + dbl.expected_method + + expect { + expect(dbl).to have_received(:expected_method).thrice + }.to raise_error(/expected: 3 times.*received: 4 times/m) + end + end end context 'ordered' do - let(:dbl) { double :one => 1, :two => 2 } + let(:dbl) { double :one => 1, :two => 2, :three => 3 } it 'passes when the messages were received in order' do dbl.one @@ -265,12 +298,13 @@ module Mocks end it 'passes with exact receive counts when received in order' do - dbl.one - dbl.one - dbl.two + 3.times { dbl.one } + 2.times { dbl.two } + dbl.three - expect(dbl).to have_received(:one).twice.ordered - expect(dbl).to have_received(:two).once.ordered + expect(dbl).to have_received(:one).thrice.ordered + expect(dbl).to have_received(:two).twice.ordered + expect(dbl).to have_received(:three).once.ordered end pending 'passes with at most receive counts when received in order' do @@ -279,6 +313,7 @@ module Mocks dbl.two expect(dbl).to have_received(:one).at_most(3).times.ordered + expect(dbl).to have_received(:one).at_most(:thrice).times.ordered expect(dbl).to have_received(:two).once.ordered end From d66bb73f73558ea71315de5cbc6e50b87223a449 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 16 Aug 2014 21:43:16 +0100 Subject: [PATCH 0688/1179] support for thrice for any_instance --- lib/rspec/mocks/any_instance/chain.rb | 1 + spec/rspec/mocks/any_instance_spec.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 4bcc1748c..6d73bcca2 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -37,6 +37,7 @@ def self.record(method_name) record :with record :once record :twice + record :thrice record :any_number_of_times record :exactly record :times diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 63efa3fdb..ff171c124 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -793,6 +793,32 @@ def foo; end end end + context "the 'thrice' constraint" do + it "passes for three invocations" do + expect_any_instance_of(klass).to receive(:foo).thrice + instance = klass.new + 3.times { instance.foo } + end + + it "fails for more than three invocations" do + expect do + expect_any_instance_of(klass).to receive(:foo).thrice + instance = klass.new + 4.times { instance.foo } + verify instance + end.to raise_error(RSpec::Mocks::MockExpectationError) + end + + it "fails for less than three invocations" do + expect do + expect_any_instance_of(klass).to receive(:foo).thrice + instance = klass.new + 2.times { instance.foo } + verify instance + end.to raise_error(RSpec::Mocks::MockExpectationError) + end + end + context "the 'exactly(n)' constraint" do it "passes for n invocations where n = 3" do expect_any_instance_of(klass).to receive(:foo).exactly(3).times From b67baddd0b1744e86c1db186da589cfcc0d8081d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 16 Aug 2014 21:43:58 +0100 Subject: [PATCH 0689/1179] changelog for #754 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 81ca6a402..c884c21b1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,8 @@ Enhancements: expectations. (Xavier Shay, #726) * Add `thrice` modifier to message expectation interface as a synonym for `exactly(3).times`. (Dennis Taylor, #753) +* Add more `thrice` synonyms e.g. `.at_least(:thrice)`, `.at_most(:thrice)`, + `receive(...).thrice` and `have_received(...).thrice`. (Jon Rowe, #754) ### 3.0.4 / 2014-08-14 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3.0.4) From 6f857a316893295423d154ab228bdd6620a5cc91 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 17 Aug 2014 11:22:02 +0100 Subject: [PATCH 0690/1179] fixup specdoc [skip ci] --- spec/rspec/mocks/matchers/have_received_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 95e3d2cca..f50865cd5 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -272,7 +272,7 @@ module Mocks }.to raise_error(/expected: 3 times.*received: 2 times/m) end - it 'fails when the message was received more than times' do + it 'fails when the message was received more than three times' do dbl = double(:expected_method => nil) dbl.expected_method dbl.expected_method From 55cbdfc79e92a68843afca5f48703a63f8d83341 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 19 Aug 2014 09:31:32 +0100 Subject: [PATCH 0691/1179] remove mention of any_number_of_times in docs [skip ci] --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 4765b9e28..982d33b23 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,6 @@ expect(double).to receive(:msg).at_least(n).times expect(double).to receive(:msg).at_most(:once) expect(double).to receive(:msg).at_most(:twice) expect(double).to receive(:msg).at_most(n).times -expect(double).to receive(:msg).any_number_of_times ``` ## Ordering From 37d78f95564b8a73e2663de28e3f2be60096464c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 19 Aug 2014 09:31:50 +0100 Subject: [PATCH 0692/1179] remove any_instance usage of any_number_of_times (which causes it to show up in docs) --- lib/rspec/mocks/any_instance/chain.rb | 1 - lib/rspec/mocks/any_instance/expectation_chain.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index 6d73bcca2..d7cefd403 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -38,7 +38,6 @@ def self.record(method_name) record :once record :twice record :thrice - record :any_number_of_times record :exactly record :times record :never diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 153f37be1..a4fd9cfdc 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -4,7 +4,7 @@ module AnyInstance # @private class ExpectationChain < Chain def expectation_fulfilled? - @expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times) + @expectation_fulfilled || constrained_to_any_of?(:never) end def initialize(*args, &block) From 39f93834e39cfc34f83b9bd87cac9425f3d97008 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 19 Aug 2014 18:12:50 +0100 Subject: [PATCH 0693/1179] changelog for #760 [skip ci] --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index c884c21b1..055f831bf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,11 @@ Enhancements: * Add more `thrice` synonyms e.g. `.at_least(:thrice)`, `.at_most(:thrice)`, `receive(...).thrice` and `have_received(...).thrice`. (Jon Rowe, #754) +Bugfixes: + +* Remove `any_number_of_times` from `any_instance` recorders that were + erroneously causing mention of the method in documentation. (Jon Rowe, #760) + ### 3.0.4 / 2014-08-14 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3.0.4) From 4469ecc734aadcae07e19c6fce2770f1030bbf36 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Tue, 19 Aug 2014 19:39:53 -0400 Subject: [PATCH 0694/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 14 +++++++++++++- script/run_build | 17 +++++++---------- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 02c95440e..3429367c6 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 35b70e134..7be1bffd0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. before_install: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e7e4e58bc..6c9da16f3 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 11ad99777..9680d0243 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html @@ -154,3 +154,15 @@ function check_documentation_coverage { function check_style_and_lint { bin/rubocop lib } + +function run_all_spec_suites { + run_specs_one_by_one + run_spec_suite_for "rspec-core" + run_spec_suite_for "rspec-expectations" + run_spec_suite_for "rspec-mocks" + run_spec_suite_for "rspec-rails" + + if rspec_support_compatible; then + run_spec_suite_for "rspec-support" + fi +} diff --git a/script/run_build b/script/run_build index b86c6ffca..f52b8f972 100755 --- a/script/run_build +++ b/script/run_build @@ -1,10 +1,15 @@ #!/bin/bash -# This file was generated on 2014-08-12T10:42:08-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x source script/functions.sh +# Allow repos to override the default functions and add their own +if [ -f script/custom_build_functions.sh ]; then + source script/custom_build_functions.sh +fi + run_specs_and_record_done run_cukes @@ -17,15 +22,7 @@ if style_and_lint_enforced; then fi if is_mri; then - run_specs_one_by_one - run_spec_suite_for "rspec-core" - run_spec_suite_for "rspec-expectations" - run_spec_suite_for "rspec-mocks" - run_spec_suite_for "rspec-rails" - - if rspec_support_compatible; then - run_spec_suite_for "rspec-support" - fi + run_all_spec_suites else echo "Skipping the rest of the build on non-MRI rubies" fi From c4ba32d70b2667cb4143d8a2876bfa7475111aa9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 Aug 2014 10:02:11 -0700 Subject: [PATCH 0695/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 5 ++++- .travis.yml | 4 +++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 3429367c6..2c2a0e4af 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. +# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects @@ -117,6 +117,9 @@ SpaceAroundEqualsInParameterDefault: StringLiterals: Enabled: false +Style/TrailingComma: + Enabled: false + TrivialAccessors: AllowDSLWriters: true AllowPredicates: true diff --git a/.travis.yml b/.travis.yml index 7be1bffd0..ff8c063bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ -# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. +# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. +language: ruby +sudo: false before_install: - "script/clone_all_rspec_repos" # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 6c9da16f3..45cb64b84 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. +# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 9680d0243..af5fa7cee 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. +# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index f52b8f972..63d97c395 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-19T19:39:53-04:00 from the rspec-dev repo. +# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From 8ba17edf476fd71803bfae19cb593bbc9f522383 Mon Sep 17 00:00:00 2001 From: Igor Kapkov Date: Sat, 23 Aug 2014 16:45:33 +0800 Subject: [PATCH 0696/1179] Fix indentation in readme --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 982d33b23..a1e0310be 100644 --- a/README.md +++ b/README.md @@ -118,21 +118,21 @@ Stubbing and verifying messages received in this way implements the Test Spy pattern. ```ruby - invitation = spy('invitation') +invitation = spy('invitation') - user.accept_invitation(invitation) +user.accept_invitation(invitation) - expect(invitation).to have_received(:accept) +expect(invitation).to have_received(:accept) - # You can also use other common message expectations. For example: - expect(invitation).to have_received(:accept).with(mailer) - expect(invitation).to have_received(:accept).twice - expect(invitation).to_not have_received(:accept).with(mailer) +# You can also use other common message expectations. For example: +expect(invitation).to have_received(:accept).with(mailer) +expect(invitation).to have_received(:accept).twice +expect(invitation).to_not have_received(:accept).with(mailer) - # One can specify a return value on the spy the same way one would a double. - invitation = spy('invitation', :accept => true) - expect(invitation).to have_received(:accept).with(mailer) - expect(invitation.accept).to eq(true) +# One can specify a return value on the spy the same way one would a double. +invitation = spy('invitation', :accept => true) +expect(invitation).to have_received(:accept).with(mailer) +expect(invitation.accept).to eq(true) ``` ## Nomenclature From 59915d5c7f81ce345a6c01f6c397d9e3b7ef7cfa Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 20 Aug 2014 15:35:34 +0100 Subject: [PATCH 0697/1179] create and_wrap_original --- lib/rspec/mocks/message_expectation.rb | 28 ++++++++-- spec/rspec/mocks/and_wrap_original_spec.rb | 63 ++++++++++++++++++++++ 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 spec/rspec/mocks/and_wrap_original_spec.rb diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 5628a16f8..35403027a 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -137,11 +137,30 @@ def yield_receiver_to_implementation_block? # counter.increment # expect(counter.count).to eq(original_count + 1) def and_call_original + and_wrap_original do |original, *args, &block| + original.call(*args, &block) + end + end + + # Decorates the stubbed method with the supplied block. The original + # unmodified method is passed to the block along with any method call + # arguments so you can delegate to it, whilst still being able to + # change what args are passed to it and/or change the return value. + # + # @note This is only available on partial doubles. + # + # @example + # + # expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block| + # original_method.call(*args, &block).first(10) + # end + # + def and_wrap_original(&block) if RSpec::Mocks::TestDouble === @method_double.object @error_generator.raise_only_valid_on_a_partial_double(:and_call_original) else warn_about_stub_override if implementation.inner_action - @implementation = AndCallOriginalImplementation.new(@method_double.original_method) + @implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block) @yield_receiver_to_implementation_block = false end end @@ -618,9 +637,10 @@ def actions # Represents an `and_call_original` implementation. # @private - class AndCallOriginalImplementation - def initialize(method) + class AndWrapOriginalImplementation + def initialize(method, block) @method = method + @block = block end CannotModifyFurtherError = Class.new(StandardError) @@ -646,7 +666,7 @@ def inner_action end def call(*args, &block) - @method.call(*args, &block) + @block.call(@method, *args, &block) end private diff --git a/spec/rspec/mocks/and_wrap_original_spec.rb b/spec/rspec/mocks/and_wrap_original_spec.rb new file mode 100644 index 000000000..c3cfd2f64 --- /dev/null +++ b/spec/rspec/mocks/and_wrap_original_spec.rb @@ -0,0 +1,63 @@ +describe "and_wrap_original" do + context "on a partial double" do + let(:klass) do + Class.new do + def results + (1..100).to_a + end + end + end + + let(:instance) { klass.new } + + it "allows us to modify the results of the original method" do + expect { + allow(instance).to receive(:results).and_wrap_original do |m| + m.call.first(10) + end + }.to change { instance.results.size }.from(100).to(10) + end + + it "raises a name error if the method does not exist" do + expect { + allow(instance).to receive(:no_results).and_wrap_original { |m| m.call } + instance.no_results + }.to raise_error NameError + end + + it "passes in the original method" do + value = nil + original_method = instance.method(:results) + allow(instance).to receive(:results).and_wrap_original { |m| value = m } + instance.results + expect(value).to eq original_method + end + + it "passes along the message arguments" do + values = nil + allow(instance).to receive(:results).and_wrap_original { |_, *args| values = args } + instance.results(1, 2, 3) + expect(values).to eq [1, 2, 3] + end + + it "passes along any supplied block" do + value = nil + allow(instance).to receive(:results).and_wrap_original { |&b| value = b } + instance.results &(block = proc {}) + expect(value).to eq block + end + + it "ignores previous stubs" do + allow(instance).to receive(:results) { "results" } + allow(instance).to receive(:results).and_wrap_original { |m| m.call } + expect(instance.results).to_not eq "results" + end + + it "can be constrained by specific arguments" do + allow(instance).to receive(:results) { :all } + allow(instance).to receive(:results).with(5).and_wrap_original { |m, n| m.call.first(n) } + expect(instance.results 5).to eq [1,2,3,4,5] + expect(instance.results).to eq :all + end + end +end From a05ebbf42a5f03200e985ab76301c8a6d2aa42f3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 21 Aug 2014 18:53:51 +0100 Subject: [PATCH 0698/1179] cuke for wrapping original implementation --- ...apping_the_original_implementation.feature | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 features/configuring_responses/wrapping_the_original_implementation.feature diff --git a/features/configuring_responses/wrapping_the_original_implementation.feature b/features/configuring_responses/wrapping_the_original_implementation.feature new file mode 100644 index 000000000..740ff65a3 --- /dev/null +++ b/features/configuring_responses/wrapping_the_original_implementation.feature @@ -0,0 +1,53 @@ +Feature: Wrapping the original implementation + + Use `and_wrap_original` to modify a partial double's original response. This can be useful + when you want to utilise an external object but mutate it's response. For example if an + API returns a large amount of data and for test purposes you'd like to trim it down. You can + also use it to configure the default response for most arguments, and then override that for + specific arguments using `with`. + + Note: `and_wrap_original` is only supported on partial doubles, as normal test doubles do + not have an original implementation. + + Background: + Given a file named "lib/api.rb" with: + """ruby + class API + def self.solve_for(x) + (1..x).to_a + end + end + """ + + Scenario: `and_wrap_original` wraps the original partial double response + Given a file named "spec/and_wrap_original_spec.rb" with: + """ruby + require 'api' + + RSpec.describe "and_wrap_original" do + it "responds as it normally would, modified by the block" do + expect(API).to receive(:solve_for).and_wrap_original { |m, *args| m.call(*args).first(5) } + expect(API.solve_for(100)).to eq [1,2,3,4,5] + end + end + """ + When I run `rspec spec/and_wrap_original_spec.rb` + Then the examples should all pass + + Scenario: `and_wrap_original` can configure a default response that can be overriden for specific args + Given a file named "spec/and_wrap_original_spec.rb" with: + """ruby + require 'api' + + RSpec.describe "and_wrap_original" do + it "can be overriden for specific arguments using #with" do + allow(API).to receive(:solve_for).and_wrap_original { |m, *args| m.call(*args).first(5) } + allow(API).to receive(:solve_for).with(2).and_return([3]) + + expect(API.solve_for(20)).to eq [1,2,3,4,5] + expect(API.solve_for(2)).to eq [3] + end + end + """ + When I run `rspec spec/and_wrap_original_spec.rb` + Then the examples should all pass From c6b4829e52ae786fa0b38b17e90c775ef07a64ea Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 23 Aug 2014 21:27:12 -0700 Subject: [PATCH 0699/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 6 +++++- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 2c2a0e4af..5a03e132a 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects @@ -117,6 +117,10 @@ SpaceAroundEqualsInParameterDefault: StringLiterals: Enabled: false +# This rule favors constant names from the English standard library which we don't load. +Style/SpecialGlobalVars: + Enabled: false + Style/TrailingComma: Enabled: false diff --git a/.travis.yml b/.travis.yml index ff8c063bc..371495bd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 45cb64b84..f57555608 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index af5fa7cee..5c7e3d0da 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index 63d97c395..10c1c2309 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-22T10:02:11-07:00 from the rspec-dev repo. +# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From c0a1b6139b061eba606e02a6a8ff957bac38c297 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 24 Aug 2014 11:01:56 +0100 Subject: [PATCH 0700/1179] Changelog for #762 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 055f831bf..a163e975d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,8 @@ Enhancements: for `exactly(3).times`. (Dennis Taylor, #753) * Add more `thrice` synonyms e.g. `.at_least(:thrice)`, `.at_most(:thrice)`, `receive(...).thrice` and `have_received(...).thrice`. (Jon Rowe, #754) +* Add `and_wrap_original` modifier for partial doubles to mutate the + response from a method. (Jon Rowe, #762) Bugfixes: From fab32275a4d02ad042b13e451cdb04da1cb44dff Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Aug 2014 22:12:36 -0700 Subject: [PATCH 0701/1179] Disable the rspec-core DSL in the global scope. --- spec/rspec/mocks/and_call_original_spec.rb | 2 +- spec/rspec/mocks/and_return_spec.rb | 2 +- spec/rspec/mocks/and_wrap_original_spec.rb | 2 +- spec/rspec/mocks/and_yield_spec.rb | 2 +- .../mocks/any_instance/message_chains_spec.rb | 2 +- spec/rspec/mocks/any_instance_spec.rb | 2 +- spec/rspec/mocks/argument_matchers_spec.rb | 2 +- spec/rspec/mocks/array_including_matcher_spec.rb | 2 +- spec/rspec/mocks/at_least_spec.rb | 2 +- spec/rspec/mocks/at_most_spec.rb | 2 +- spec/rspec/mocks/before_all_spec.rb | 2 +- spec/rspec/mocks/block_return_value_spec.rb | 2 +- .../combining_implementation_instructions_spec.rb | 2 +- spec/rspec/mocks/configuration_spec.rb | 2 +- spec/rspec/mocks/double_spec.rb | 2 +- spec/rspec/mocks/example_methods_spec.rb | 2 +- spec/rspec/mocks/expiration_spec.rb | 2 +- spec/rspec/mocks/hash_excluding_matcher_spec.rb | 2 +- spec/rspec/mocks/hash_including_matcher_spec.rb | 2 +- spec/rspec/mocks/instance_method_stasher_spec.rb | 2 +- spec/rspec/mocks/marshal_extension_spec.rb | 2 +- spec/rspec/mocks/matchers/have_received_spec.rb | 2 +- .../mocks/matchers/receive_message_chain_spec.rb | 2 +- spec/rspec/mocks/matchers/receive_messages_spec.rb | 14 +++++++------- spec/rspec/mocks/matchers/receive_spec.rb | 2 +- spec/rspec/mocks/methods_spec.rb | 2 +- spec/rspec/mocks/mock_expectation_error_spec.rb | 2 +- spec/rspec/mocks/mock_ordering_spec.rb | 2 +- spec/rspec/mocks/multiple_return_value_spec.rb | 10 +++++----- spec/rspec/mocks/mutate_const_spec.rb | 4 ++-- spec/rspec/mocks/nil_expectation_warning_spec.rb | 4 ++-- spec/rspec/mocks/null_object_double_spec.rb | 10 +++++----- spec/rspec/mocks/once_counts_spec.rb | 2 +- spec/rspec/mocks/order_group_spec.rb | 2 +- spec/rspec/mocks/partial_double_spec.rb | 12 ++++++------ .../partial_double_using_mocks_directly_spec.rb | 2 +- spec/rspec/mocks/precise_counts_spec.rb | 2 +- spec/rspec/mocks/serialization_spec.rb | 2 +- spec/rspec/mocks/space_spec.rb | 2 +- spec/rspec/mocks/spy_spec.rb | 2 +- spec/rspec/mocks/stash_spec.rb | 4 ++-- spec/rspec/mocks/stub_chain_spec.rb | 2 +- spec/rspec/mocks/stub_implementation_spec.rb | 4 ++-- spec/rspec/mocks/stub_spec.rb | 4 ++-- .../mocks/stubbed_message_expectations_spec.rb | 2 +- .../mocks/syntax_agnostic_message_matchers_spec.rb | 4 ++-- spec/rspec/mocks/test_double_spec.rb | 2 +- spec/rspec/mocks/thrice_counts_spec.rb | 2 +- spec/rspec/mocks/to_ary_spec.rb | 2 +- spec/rspec/mocks/twice_counts_spec.rb | 2 +- spec/rspec/mocks/verifying_double_spec.rb | 2 +- .../mocks/verifying_message_expecation_spec.rb | 2 +- spec/rspec/mocks_spec.rb | 2 +- spec/spec_helper.rb | 9 +++++---- spec/support/before_all_shared_example_group.rb | 2 +- 55 files changed, 84 insertions(+), 83 deletions(-) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index a923d60e9..398002cd6 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -1,6 +1,6 @@ require 'delegate' -describe "and_call_original" do +RSpec.describe "and_call_original" do context "on a partial double" do let(:klass) do Class.new do diff --git a/spec/rspec/mocks/and_return_spec.rb b/spec/rspec/mocks/and_return_spec.rb index 070ea10e0..a05c28070 100644 --- a/spec/rspec/mocks/and_return_spec.rb +++ b/spec/rspec/mocks/and_return_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe 'and_return' do + RSpec.describe 'and_return' do let(:obj) { double('obj') } context 'when a block is passed' do diff --git a/spec/rspec/mocks/and_wrap_original_spec.rb b/spec/rspec/mocks/and_wrap_original_spec.rb index c3cfd2f64..75ea3eec7 100644 --- a/spec/rspec/mocks/and_wrap_original_spec.rb +++ b/spec/rspec/mocks/and_wrap_original_spec.rb @@ -1,4 +1,4 @@ -describe "and_wrap_original" do +RSpec.describe "and_wrap_original" do context "on a partial double" do let(:klass) do Class.new do diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index 0e7200928..af22adef9 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -1,4 +1,4 @@ -describe RSpec::Mocks::Double do +RSpec.describe RSpec::Mocks::Double do let(:obj) { double } diff --git a/spec/rspec/mocks/any_instance/message_chains_spec.rb b/spec/rspec/mocks/any_instance/message_chains_spec.rb index b2fe19865..723205ee7 100644 --- a/spec/rspec/mocks/any_instance/message_chains_spec.rb +++ b/spec/rspec/mocks/any_instance/message_chains_spec.rb @@ -1,4 +1,4 @@ -describe RSpec::Mocks::AnyInstance::MessageChains do +RSpec.describe RSpec::Mocks::AnyInstance::MessageChains do let(:recorder) { double } let(:chains) { RSpec::Mocks::AnyInstance::MessageChains.new } let(:stub_chain) { RSpec::Mocks::AnyInstance::StubChain.new recorder } diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index ff171c124..cdc51b189 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -2,7 +2,7 @@ module RSpec module Mocks - describe "#any_instance" do + RSpec.describe "#any_instance" do class CustomErrorForAnyInstanceSpec < StandardError;end let(:klass) do diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index 8c2c755dd..b3ef95abc 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "argument matchers matching" do + RSpec.describe "argument matchers matching" do let(:a_double) { double } after(:each, :reset => true) do diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb index 77c59f84d..9dac97b91 100644 --- a/spec/rspec/mocks/array_including_matcher_spec.rb +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module ArgumentMatchers - describe ArrayIncludingMatcher do + RSpec.describe ArrayIncludingMatcher do it "describes itself properly" do expect(ArrayIncludingMatcher.new([1, 2, 3]).description).to eq "array_including(1, 2, 3)" end diff --git a/spec/rspec/mocks/at_least_spec.rb b/spec/rspec/mocks/at_least_spec.rb index c8d394470..e65dc1298 100644 --- a/spec/rspec/mocks/at_least_spec.rb +++ b/spec/rspec/mocks/at_least_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "at_least" do + RSpec.describe "at_least" do before(:each) { @double = double } it "fails if method is never called" do diff --git a/spec/rspec/mocks/at_most_spec.rb b/spec/rspec/mocks/at_most_spec.rb index 8b92a4a10..3442e678b 100644 --- a/spec/rspec/mocks/at_most_spec.rb +++ b/spec/rspec/mocks/at_most_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "at_most" do + RSpec.describe "at_most" do before(:each) do @double = double end diff --git a/spec/rspec/mocks/before_all_spec.rb b/spec/rspec/mocks/before_all_spec.rb index 4c1e47546..44ccb9702 100644 --- a/spec/rspec/mocks/before_all_spec.rb +++ b/spec/rspec/mocks/before_all_spec.rb @@ -1,6 +1,6 @@ require 'support/before_all_shared_example_group' -describe "Using rspec-mocks features in before(:all) blocks" do +RSpec.describe "Using rspec-mocks features in before(:all) blocks" do describe "#stub_const" do include_examples "fails in a before(:all) block" do def use_rspec_mocks diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 2b5a481ed..5a16ca143 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -1,4 +1,4 @@ -describe "a double declaration with a block handed to:" do +RSpec.describe "a double declaration with a block handed to:" do describe "expect(...).to receive" do it "returns the value of executing the block" do obj = Object.new diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 54d475c02..e2a2c1c24 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "Combining implementation instructions" do + RSpec.describe "Combining implementation instructions" do it 'can combine and_yield and and_return' do dbl = double allow(dbl).to receive(:foo).and_yield(5).and_return(3) diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index e26b3ccd2..724bfcf9f 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe Configuration do + RSpec.describe Configuration do let(:config) { Configuration.new } let(:mod_1) { Module.new } let(:mod_2) { Module.new } diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 17f81290c..1f35d6c5c 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe Double do + RSpec.describe Double do before(:each) { @double = double("test double") } after(:each) { reset @double } diff --git a/spec/rspec/mocks/example_methods_spec.rb b/spec/rspec/mocks/example_methods_spec.rb index 71e51f577..157e5883f 100644 --- a/spec/rspec/mocks/example_methods_spec.rb +++ b/spec/rspec/mocks/example_methods_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe ExampleMethods do + RSpec.describe ExampleMethods do it 'does not define private helper methods since it gets included into a ' + 'namespace where users define methods and could inadvertently overwrite ' + 'them' do diff --git a/spec/rspec/mocks/expiration_spec.rb b/spec/rspec/mocks/expiration_spec.rb index 5d93345c8..d6d3b376d 100644 --- a/spec/rspec/mocks/expiration_spec.rb +++ b/spec/rspec/mocks/expiration_spec.rb @@ -1,7 +1,7 @@ module RSpec module Mocks RSpec.describe "After a test double has been torn down" do - shared_examples_for "expiration" do + RSpec.shared_examples_for "expiration" do before do expect(dbl).to receive(:foo).at_least(:once) allow(dbl).to receive(:bar) diff --git a/spec/rspec/mocks/hash_excluding_matcher_spec.rb b/spec/rspec/mocks/hash_excluding_matcher_spec.rb index 4ef86c954..c4e52fd64 100644 --- a/spec/rspec/mocks/hash_excluding_matcher_spec.rb +++ b/spec/rspec/mocks/hash_excluding_matcher_spec.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module ArgumentMatchers - describe HashExcludingMatcher do + RSpec.describe HashExcludingMatcher do it "describes itself properly" do expect(HashExcludingMatcher.new(:a => 5).description).to eq "hash_not_including(:a=>5)" diff --git a/spec/rspec/mocks/hash_including_matcher_spec.rb b/spec/rspec/mocks/hash_including_matcher_spec.rb index b58912532..9882cba31 100644 --- a/spec/rspec/mocks/hash_including_matcher_spec.rb +++ b/spec/rspec/mocks/hash_including_matcher_spec.rb @@ -1,7 +1,7 @@ module RSpec module Mocks module ArgumentMatchers - describe HashIncludingMatcher do + RSpec.describe HashIncludingMatcher do it "describes itself properly" do expect(HashIncludingMatcher.new(:a => 1).description).to eq "hash_including(:a=>1)" diff --git a/spec/rspec/mocks/instance_method_stasher_spec.rb b/spec/rspec/mocks/instance_method_stasher_spec.rb index a63924169..ac2eadb92 100644 --- a/spec/rspec/mocks/instance_method_stasher_spec.rb +++ b/spec/rspec/mocks/instance_method_stasher_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe InstanceMethodStasher do + RSpec.describe InstanceMethodStasher do class ExampleClass def hello :hello_defined_on_class diff --git a/spec/rspec/mocks/marshal_extension_spec.rb b/spec/rspec/mocks/marshal_extension_spec.rb index ef8aad076..a44f6368f 100644 --- a/spec/rspec/mocks/marshal_extension_spec.rb +++ b/spec/rspec/mocks/marshal_extension_spec.rb @@ -1,4 +1,4 @@ -describe Marshal, 'extensions' do +RSpec.describe Marshal, 'extensions' do # An object that raises when code attempts to dup it. # # Because we manipulate the internals of RSpec::Mocks.space below, we need diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index f50865cd5..c56e3b55b 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe Matchers::HaveReceived do + RSpec.describe Matchers::HaveReceived do describe "expect(...).to have_received" do it 'passes when the double has received the given message' do dbl = double_with_met_expectation(:expected_method) diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index c5620149a..789db3b00 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -1,5 +1,5 @@ module RSpec::Mocks::Matchers - describe "receive_message_chain" do + RSpec.describe "receive_message_chain" do let(:object) { double(:object) } context "with only the expect syntax enabled" do diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 4155aab89..a645c1d2f 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - shared_examples "complains when given blocks" do + RSpec.shared_examples "complains when given blocks" do it "complains if a { } block is given" do expect { target.to receive_messages(:a => 1) { "implementation" } @@ -16,7 +16,7 @@ module Mocks end end - shared_examples "handles partially mocked objects correctly" do + RSpec.shared_examples "handles partially mocked objects correctly" do let(:obj) { Struct.new(:a).new('original') } it "resets partially mocked objects correctly" do @@ -28,7 +28,7 @@ module Mocks end end - describe "allow(...).to receive_messages(:a => 1, :b => 2)" do + RSpec.describe "allow(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { double "Object" } let(:target) { allow(obj) } @@ -42,7 +42,7 @@ module Mocks it_behaves_like "handles partially mocked objects correctly" end - describe "allow_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do + RSpec.describe "allow_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { Object.new } let(:target) { allow_any_instance_of(Object) } @@ -64,7 +64,7 @@ module Mocks it_behaves_like "complains when given blocks" end - describe "expect(...).to receive_messages(:a => 1, :b => 2)" do + RSpec.describe "expect(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { double "Object" } let(:target) { expect(obj) } @@ -99,7 +99,7 @@ module Mocks it_behaves_like "handles partially mocked objects correctly" end - describe "expect_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do + RSpec.describe "expect_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { Object.new } let(:target) { expect_any_instance_of(Object) } @@ -113,7 +113,7 @@ module Mocks it_behaves_like "complains when given blocks" end - describe "negative expectation failure" do + RSpec.describe "negative expectation failure" do let(:obj) { Object.new } example "allow(...).to_not receive_messages(:a => 1, :b => 2)" do diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 749b26522..b896bad60 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe Matchers::Receive do + RSpec.describe Matchers::Receive do include_context "with syntax", :expect describe "expectations/allowances on any instance recorders" do diff --git a/spec/rspec/mocks/methods_spec.rb b/spec/rspec/mocks/methods_spec.rb index 7a25fb4c7..09cb1dd91 100644 --- a/spec/rspec/mocks/methods_spec.rb +++ b/spec/rspec/mocks/methods_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "Methods added to every object" do + RSpec.describe "Methods added to every object" do include_context "with syntax", :expect def added_methods diff --git a/spec/rspec/mocks/mock_expectation_error_spec.rb b/spec/rspec/mocks/mock_expectation_error_spec.rb index 342a554ea..49211d5f4 100644 --- a/spec/rspec/mocks/mock_expectation_error_spec.rb +++ b/spec/rspec/mocks/mock_expectation_error_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe 'MockExpectationError' do + RSpec.describe 'MockExpectationError' do class Foo def self.foo diff --git a/spec/rspec/mocks/mock_ordering_spec.rb b/spec/rspec/mocks/mock_ordering_spec.rb index ea7327f79..57821caef 100644 --- a/spec/rspec/mocks/mock_ordering_spec.rb +++ b/spec/rspec/mocks/mock_ordering_spec.rb @@ -1,7 +1,7 @@ module RSpec module Mocks - describe "ordering" do + RSpec.describe "ordering" do before { @double = double("test double") } after { reset @double } diff --git a/spec/rspec/mocks/multiple_return_value_spec.rb b/spec/rspec/mocks/multiple_return_value_spec.rb index 4c539917e..6fc86f114 100644 --- a/spec/rspec/mocks/multiple_return_value_spec.rb +++ b/spec/rspec/mocks/multiple_return_value_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "a double stubbed with multiple return values" do + RSpec.describe "a double stubbed with multiple return values" do let(:a_double) { double } before do @@ -13,7 +13,7 @@ module Mocks end end - describe "a message expectation with multiple return values and no specified count" do + RSpec.describe "a message expectation with multiple return values and no specified count" do before(:each) do @double = double @return_values = [1,2,3] @@ -50,7 +50,7 @@ module Mocks end end - describe "a message expectation with multiple return values with a specified count equal to the number of values" do + RSpec.describe "a message expectation with multiple return values with a specified count equal to the number of values" do before(:each) do @double = double @return_values = [1,2,3] @@ -65,7 +65,7 @@ module Mocks end end - describe "a message expectation with multiple return values specifying at_least less than the number of values" do + RSpec.describe "a message expectation with multiple return values specifying at_least less than the number of values" do before(:each) do @double = double expect(@double).to receive(:do_something).at_least(:twice).with(no_args).and_return(11, 22) @@ -100,7 +100,7 @@ module Mocks end end - describe "a message expectation with multiple return values with a specified count larger than the number of values" do + RSpec.describe "a message expectation with multiple return values with a specified count larger than the number of values" do before(:each) do @double = RSpec::Mocks::Double.new("double") expect(@double).to receive(:do_something).exactly(3).times.and_return(11, 22) diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 157ea966b..b80cb7de6 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -23,7 +23,7 @@ class TestSubClass < TestClass module RSpec module Mocks - describe "Constant Mutating" do + RSpec.describe "Constant Mutating" do include RSpec::Support::RecursiveConstMethods def reset_rspec_mocks @@ -433,7 +433,7 @@ def change_const_value_to(value) end end - describe Constant do + RSpec.describe Constant do describe ".original" do context 'for a previously defined unstubbed constant' do let(:const) { Constant.original("TestClass::M") } diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index c4dff2b92..9e95e079a 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "an expectation set on nil" do + RSpec.describe "an expectation set on nil" do it "issues a warning with file and line number information" do expected_warning = %r%An expectation of :foo was set on nil. Called from #{__FILE__}:#{__LINE__+3}(:in .+)?. Use allow_message_expectations_on_nil to disable warnings.% expect(Kernel).to receive(:warn).with(expected_warning) @@ -31,7 +31,7 @@ module Mocks end end - describe "#allow_message_expectations_on_nil" do + RSpec.describe "#allow_message_expectations_on_nil" do include_context "with monkey-patched marshal" it "does not affect subsequent examples" do diff --git a/spec/rspec/mocks/null_object_double_spec.rb b/spec/rspec/mocks/null_object_double_spec.rb index 495fcad29..09e2d68e4 100644 --- a/spec/rspec/mocks/null_object_double_spec.rb +++ b/spec/rspec/mocks/null_object_double_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "a double _not_ acting as a null object" do + RSpec.describe "a double _not_ acting as a null object" do before(:each) do @double = double('non-null object') end @@ -25,7 +25,7 @@ module Mocks end end - describe "a double acting as a null object" do + RSpec.describe "a double acting as a null object" do before(:each) do @double = double('null object').as_null_object end @@ -109,21 +109,21 @@ module Mocks end end - describe "#as_null_object" do + RSpec.describe "#as_null_object" do it "sets the object to null_object" do obj = double('anything').as_null_object expect(obj).to be_null_object end end - describe "#null_object?" do + RSpec.describe "#null_object?" do it "defaults to false" do obj = double('anything') expect(obj).not_to be_null_object end end - describe "when using the :expect syntax" do + RSpec.describe "when using the :expect syntax" do include_context "with syntax", :expect it 'still supports null object doubles' do diff --git a/spec/rspec/mocks/once_counts_spec.rb b/spec/rspec/mocks/once_counts_spec.rb index 825cc3c0b..fd4ae01ba 100644 --- a/spec/rspec/mocks/once_counts_spec.rb +++ b/spec/rspec/mocks/once_counts_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "#once" do + RSpec.describe "#once" do before(:each) do @double = double end diff --git a/spec/rspec/mocks/order_group_spec.rb b/spec/rspec/mocks/order_group_spec.rb index adf052a48..27a58d386 100644 --- a/spec/rspec/mocks/order_group_spec.rb +++ b/spec/rspec/mocks/order_group_spec.rb @@ -1,4 +1,4 @@ -describe 'OrderGroup' do +RSpec.describe 'OrderGroup' do let(:order_group) { ::RSpec::Mocks::OrderGroup.new } describe '#consume' do diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 2b5594ea4..57b2ad55c 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "A partial double" do + RSpec.describe "A partial double" do let(:object) { Object.new } it 'does not create an any_instance recorder when a message is allowed' do @@ -137,7 +137,7 @@ def self.inspect end end - describe "Using a partial mock on a proxy object", :if => defined?(::BasicObject) do + RSpec.describe "Using a partial mock on a proxy object", :if => defined?(::BasicObject) do let(:proxy_class) do Class.new(::BasicObject) do def initialize(target) @@ -175,7 +175,7 @@ def method_missing(*a) end end - describe "Partially mocking an object that defines ==, after another mock has been defined" do + RSpec.describe "Partially mocking an object that defines ==, after another mock has been defined" do before(:each) do double("existing mock", :foo => :foo) end @@ -199,7 +199,7 @@ def ==(other) end end - describe "A partial class mock that has been subclassed" do + RSpec.describe "A partial class mock that has been subclassed" do let(:klass) { Class.new } let(:subklass) { Class.new(klass) } @@ -228,7 +228,7 @@ def ==(other) end end - describe "Method visibility when using partial mocks" do + RSpec.describe "Method visibility when using partial mocks" do let(:klass) do Class.new do def public_method @@ -264,7 +264,7 @@ def private_method; end end - describe 'when verify_partial_doubles configuration option is set' do + RSpec.describe 'when verify_partial_doubles configuration option is set' do include_context "with isolated configuration" let(:klass) do diff --git a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb index 8b7eac593..605a16e8c 100644 --- a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb +++ b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb @@ -1,5 +1,5 @@ module RSpec::Mocks - describe "PartialDoubleUsingMocksDirectly" do + RSpec.describe "PartialDoubleUsingMocksDirectly" do let(:klass) do Class.new do module MethodMissing diff --git a/spec/rspec/mocks/precise_counts_spec.rb b/spec/rspec/mocks/precise_counts_spec.rb index b72406fb1..77227bdb5 100644 --- a/spec/rspec/mocks/precise_counts_spec.rb +++ b/spec/rspec/mocks/precise_counts_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "PreciseCounts" do + RSpec.describe "PreciseCounts" do before(:each) do @double = double("test double") end diff --git a/spec/rspec/mocks/serialization_spec.rb b/spec/rspec/mocks/serialization_spec.rb index a3c60f241..af289062e 100644 --- a/spec/rspec/mocks/serialization_spec.rb +++ b/spec/rspec/mocks/serialization_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "Serialization of mocked objects" do + RSpec.describe "Serialization of mocked objects" do include_context "with monkey-patched marshal" class SerializableObject < Struct.new(:foo, :bar); end diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index f4f67db05..c5ce5a10c 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -1,6 +1,6 @@ module RSpec::Mocks - describe Space do + RSpec.describe Space do let(:space) { Space.new } let(:dbl_1) { Object.new } let(:dbl_2) { Object.new } diff --git a/spec/rspec/mocks/spy_spec.rb b/spec/rspec/mocks/spy_spec.rb index 08babd310..57868a234 100644 --- a/spec/rspec/mocks/spy_spec.rb +++ b/spec/rspec/mocks/spy_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe "the spy family of methods" do +RSpec.describe "the spy family of methods" do describe "spy" do it "responds to arbitrary methods" do expect(spy.respond_to?(:foo)).to be true diff --git a/spec/rspec/mocks/stash_spec.rb b/spec/rspec/mocks/stash_spec.rb index df76ca131..93a202995 100644 --- a/spec/rspec/mocks/stash_spec.rb +++ b/spec/rspec/mocks/stash_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "only stashing the original method" do + RSpec.describe "only stashing the original method" do let(:klass) do Class.new do def self.foo(arg) @@ -22,7 +22,7 @@ def self.foo(arg) end end - describe "when a class method is aliased on a subclass and the method is mocked" do + RSpec.describe "when a class method is aliased on a subclass and the method is mocked" do it "restores the original aliased public method" do klass = Class.new do class << self diff --git a/spec/rspec/mocks/stub_chain_spec.rb b/spec/rspec/mocks/stub_chain_spec.rb index c491bed92..1db7d2722 100644 --- a/spec/rspec/mocks/stub_chain_spec.rb +++ b/spec/rspec/mocks/stub_chain_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "A chained method stub" do + RSpec.describe "A chained method stub" do let(:object) { Object.new } it 'does not get confused by symbol vs string usage for the messages' do diff --git a/spec/rspec/mocks/stub_implementation_spec.rb b/spec/rspec/mocks/stub_implementation_spec.rb index 0c94efeea..9507f3830 100644 --- a/spec/rspec/mocks/stub_implementation_spec.rb +++ b/spec/rspec/mocks/stub_implementation_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "stub implementation" do + RSpec.describe "stub implementation" do describe "with no args" do it "execs the block when called" do obj = double() @@ -26,7 +26,7 @@ module Mocks end end - describe "unstubbing with `and_call_original`" do + RSpec.describe "unstubbing with `and_call_original`" do it "replaces the stubbed method with the original method" do obj = Object.new def obj.foo; :original; end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 2c226d94c..ee9fd0278 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "A method stub" do + RSpec.describe "A method stub" do before(:each) do @class = Class.new do class << self @@ -428,7 +428,7 @@ def object.existing_method; :original_value; end end end - describe "A method stub with args" do + RSpec.describe "A method stub with args" do before(:each) do @stub = Object.new allow(@stub).to receive(:foo).with("bar") diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index 04a9d14de..53cf48fff 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -1,4 +1,4 @@ -describe "expection set on previously stubbed method" do +RSpec.describe "expection set on previously stubbed method" do it "fails if message is not received after expectation is set" do double = double(:msg => nil) double.msg diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index 93a86757a..678650e47 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -1,7 +1,7 @@ module RSpec module Mocks - describe ".allow_message" do + RSpec.describe ".allow_message" do let(:subject) { Object.new } it "sets up basic message allowance" do @@ -45,7 +45,7 @@ module Mocks end end - describe ".expect_message" do + RSpec.describe ".expect_message" do let(:subject) { Object.new } it "sets up basic message expectation, verifies as uncalled" do diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 6aff70f07..a260d35dc 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe TestDouble do + RSpec.describe TestDouble do describe "#freeze" do subject { double } diff --git a/spec/rspec/mocks/thrice_counts_spec.rb b/spec/rspec/mocks/thrice_counts_spec.rb index 00147e43b..478812b77 100644 --- a/spec/rspec/mocks/thrice_counts_spec.rb +++ b/spec/rspec/mocks/thrice_counts_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "#thrice" do + RSpec.describe "#thrice" do before(:each) do @double = double("test double") end diff --git a/spec/rspec/mocks/to_ary_spec.rb b/spec/rspec/mocks/to_ary_spec.rb index 3a28a2aee..c6b178c3c 100644 --- a/spec/rspec/mocks/to_ary_spec.rb +++ b/spec/rspec/mocks/to_ary_spec.rb @@ -1,4 +1,4 @@ -describe "a double receiving to_ary" do +RSpec.describe "a double receiving to_ary" do shared_examples "to_ary" do it "can be overridden with a stub" do allow(obj).to receive(:to_ary) { :non_nil_value } diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index e6c2ebeae..556fcd2ef 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe "#twice" do + RSpec.describe "#twice" do before(:each) do @double = double("test double") end diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb index a9a88a2d5..937da7cfa 100644 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ b/spec/rspec/mocks/verifying_double_spec.rb @@ -70,7 +70,7 @@ def defined_private_method module RSpec module Mocks - describe 'verifying doubles' do + RSpec.describe 'verifying doubles' do describe 'instance doubles' do describe 'when doubled class is not loaded' do include_context "with isolated configuration" diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb index 66e0b60c4..cf9d5ad64 100644 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ b/spec/rspec/mocks/verifying_message_expecation_spec.rb @@ -1,6 +1,6 @@ module RSpec module Mocks - describe VerifyingMessageExpectation do + RSpec.describe VerifyingMessageExpectation do describe '#with' do let(:error_generator) { double.as_null_object } let(:string_module_reference) { DirectModuleReference.new(String) } diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 3650b9a6c..97fd6618e 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -1,6 +1,6 @@ require 'rspec/support/spec/prevent_load_time_warnings' -describe RSpec::Mocks do +RSpec.describe RSpec::Mocks do it_behaves_like 'a library that issues no warnings when loaded', 'rspec-mocks', 'require "rspec/mocks"', # Must be required before other files due to how our autoloads are setup. diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6fc3917ca..58b15bdb2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -51,6 +51,7 @@ def prevents(msg = //, &block) require 'rspec/support/spec' RSpec.configure do |config| + config.expose_dsl_globally = false config.mock_with :rspec config.color = true config.order = :random @@ -80,7 +81,7 @@ def prevents(msg = //, &block) config.include RSpec::Support::RubyFeatures end -shared_context "with syntax" do |syntax| +RSpec.shared_context "with syntax" do |syntax| orig_syntax = nil before(:all) do @@ -94,7 +95,7 @@ def prevents(msg = //, &block) end -shared_context "with isolated configuration" do +RSpec.shared_context "with isolated configuration" do orig_configuration = nil before do orig_configuration = RSpec::Mocks.configuration @@ -106,7 +107,7 @@ def prevents(msg = //, &block) end end -shared_context "with monkey-patched marshal" do +RSpec.shared_context "with monkey-patched marshal" do before do RSpec::Mocks.configuration.patch_marshal_to_support_partial_doubles = true end @@ -116,7 +117,7 @@ def prevents(msg = //, &block) end end -shared_context "with the default mocks syntax" do +RSpec.shared_context "with the default mocks syntax" do orig_syntax = nil before(:all) do diff --git a/spec/support/before_all_shared_example_group.rb b/spec/support/before_all_shared_example_group.rb index e9dc3226c..ecdba0657 100644 --- a/spec/support/before_all_shared_example_group.rb +++ b/spec/support/before_all_shared_example_group.rb @@ -1,4 +1,4 @@ -shared_examples "fails in a before(:all) block" do +RSpec.shared_examples "fails in a before(:all) block" do the_error = nil before(:all) do begin From 2e4bf38ac54c7908cbbb69325230cd513f1aaa7e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Aug 2014 22:38:00 -0700 Subject: [PATCH 0702/1179] Fix ruby warning. --- spec/rspec/mocks/and_wrap_original_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/and_wrap_original_spec.rb b/spec/rspec/mocks/and_wrap_original_spec.rb index 75ea3eec7..e66003c3e 100644 --- a/spec/rspec/mocks/and_wrap_original_spec.rb +++ b/spec/rspec/mocks/and_wrap_original_spec.rb @@ -43,7 +43,7 @@ def results it "passes along any supplied block" do value = nil allow(instance).to receive(:results).and_wrap_original { |&b| value = b } - instance.results &(block = proc {}) + instance.results(&(block = proc {})) expect(value).to eq block end From a1b93d08acd045fcaddd11b6efd24fa64a7d49a6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 27 Aug 2014 21:38:15 -0700 Subject: [PATCH 0703/1179] Setup rubocop. Closes #702. --- .rubocop.yml | 17 ++++++ Gemfile | 1 + lib/rspec/mocks/any_instance/chain.rb | 2 +- .../mocks/any_instance/expectation_chain.rb | 3 +- .../mocks/any_instance/message_chains.rb | 15 ++--- lib/rspec/mocks/any_instance/proxy.rb | 4 +- lib/rspec/mocks/any_instance/recorder.rb | 52 ++++++++--------- lib/rspec/mocks/any_instance/stub_chain.rb | 8 +-- lib/rspec/mocks/argument_list_matcher.rb | 8 +-- lib/rspec/mocks/argument_matchers.rb | 9 ++- lib/rspec/mocks/configuration.rb | 13 +---- lib/rspec/mocks/error_generator.rb | 56 ++++++++++--------- lib/rspec/mocks/example_methods.rb | 8 +-- lib/rspec/mocks/instance_method_stasher.rb | 10 ++-- lib/rspec/mocks/matchers/have_received.rb | 15 +++-- lib/rspec/mocks/matchers/receive.rb | 16 +++--- .../mocks/matchers/receive_message_chain.rb | 9 ++- lib/rspec/mocks/matchers/receive_messages.rb | 13 ++--- lib/rspec/mocks/message_chain.rb | 10 ++-- lib/rspec/mocks/message_expectation.rb | 42 +++++++------- lib/rspec/mocks/method_double.rb | 27 +++++---- lib/rspec/mocks/method_reference.rb | 15 ++--- lib/rspec/mocks/mutate_const.rb | 42 +++++++------- lib/rspec/mocks/object_reference.rb | 25 ++++----- lib/rspec/mocks/order_group.rb | 9 ++- lib/rspec/mocks/proxy.rb | 46 +++++++-------- lib/rspec/mocks/space.rb | 48 ++++++++-------- lib/rspec/mocks/syntax.rb | 16 +++--- lib/rspec/mocks/targets.rb | 12 ++-- lib/rspec/mocks/test_double.rb | 6 +- lib/rspec/mocks/verifying_double.rb | 22 ++++---- .../mocks/verifying_message_expecation.rb | 16 +++--- lib/rspec/mocks/verifying_proxy.rb | 24 ++++---- 33 files changed, 312 insertions(+), 307 deletions(-) create mode 100644 .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 000000000..70794c629 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,17 @@ +inherit_from: .rubocop_rspec_base.yml + +# This should go down over time. +Style/ClassLength: + Max: 278 + +# This should go down over time. +Style/CyclomaticComplexity: + Max: 18 + +# This should go down over time. +Style/LineLength: + Max: 193 + +# This should go down over time. +Style/MethodLength: + Max: 49 diff --git a/Gemfile b/Gemfile index 0c9b59edf..3bd498c10 100644 --- a/Gemfile +++ b/Gemfile @@ -13,6 +13,7 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp end gem 'yard', '~> 0.8.7', :require => false +gem 'rubocop', "~> 0.23.0", :platform => [:ruby_19, :ruby_20, :ruby_21] ### deps for rdoc.info group :documentation do diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index d7cefd403..ebc3e4f9e 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -84,7 +84,7 @@ def with(*args, &block) super end - private + private def negated? messages.any? { |(message, *_), _| message == :never } diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index a4fd9cfdc..02b55e8d6 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -14,13 +14,12 @@ def initialize(*args, &block) private - def verify_invocation_order(rspec_method_name, *args, &block) + def verify_invocation_order(_rspec_method_name, *_args, &_block) end end # @private class PositiveExpectationChain < ExpectationChain - private def create_message_expectation_on(instance) diff --git a/lib/rspec/mocks/any_instance/message_chains.rb b/lib/rspec/mocks/any_instance/message_chains.rb index 067096938..0ce39bc8d 100644 --- a/lib/rspec/mocks/any_instance/message_chains.rb +++ b/lib/rspec/mocks/any_instance/message_chains.rb @@ -35,15 +35,13 @@ def has_expectation?(method_name) # @private def each_unfulfilled_expectation_matching(method_name, *args) @chains_by_method_name[method_name].each do |chain| - if !chain.expectation_fulfilled? && chain.matches_args?(*args) - yield chain - end + yield chain if !chain.expectation_fulfilled? && chain.matches_args?(*args) end end # @private def all_expectations_fulfilled? - @chains_by_method_name.all? do |method_name, chains| + @chains_by_method_name.all? do |_method_name, chains| chains.all? { |chain| chain.expectation_fulfilled? } end end @@ -74,9 +72,12 @@ def playback!(instance, method_name) def raise_if_second_instance_to_receive_message(instance) @instance_with_expectation ||= instance if ExpectationChain === instance - if ExpectationChain === instance && !@instance_with_expectation.equal?(instance) - raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" - end + return unless ExpectationChain === instance + return if @instance_with_expectation.equal?(instance) + + raise RSpec::Mocks::MockExpectationError, + "Exactly one instance should have received the following " \ + "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" end end end diff --git a/lib/rspec/mocks/any_instance/proxy.rb b/lib/rspec/mocks/any_instance/proxy.rb index 5f7831c3b..fc66d392a 100644 --- a/lib/rspec/mocks/any_instance/proxy.rb +++ b/lib/rspec/mocks/any_instance/proxy.rb @@ -97,11 +97,11 @@ def initialize(targets) end if RUBY_VERSION.to_f > 1.8 - def respond_to_missing?(method_name, include_private = false) + def respond_to_missing?(method_name, include_private=false) super || @targets.first.respond_to?(method_name, include_private) end else - def respond_to?(method_name, include_private = false) + def respond_to?(method_name, include_private=false) super || @targets.first.respond_to?(method_name, include_private) end end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 17c91efc0..19c210a6c 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -15,7 +15,7 @@ class Recorder def initialize(klass) @message_chains = MessageChains.new - @stubs = Hash.new { |hash,key| hash[key] = [] } + @stubs = Hash.new { |hash, key| hash[key] = [] } @observed_methods = [] @played_methods = {} @klass = klass @@ -88,14 +88,17 @@ def unstub(method_name) # Used internally to verify that message expectations have been # fulfilled. def verify - if @expectation_set && !message_chains.all_expectations_fulfilled? - raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}" - end + return unless @expectation_set + return if message_chains.all_expectations_fulfilled? + + raise RSpec::Mocks::MockExpectationError, + "Exactly one instance should have received the following " \ + "message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}" end # @private def stop_all_observation! - @observed_methods.each {|method_name| restore_method!(method_name)} + @observed_methods.each { |method_name| restore_method!(method_name) } end # @private @@ -122,7 +125,7 @@ def already_observing?(method_name) end # @private - def notify_received_message(object, message, args, blk) + def notify_received_message(_object, message, args, _blk) has_expectation = false message_chains.each_unfulfilled_expectation_matching(message, *args) do |expectation| @@ -130,10 +133,10 @@ def notify_received_message(object, message, args, blk) expectation.expectation_fulfilled! end - if has_expectation - restore_method!(message) - mark_invoked!(message) - end + return unless has_expectation + + restore_method!(message) + mark_invoked!(message) end protected @@ -167,7 +170,7 @@ def super_class_observing?(method_name) end def normalize_chain(*args) - args.shift.to_s.split('.').map {|s| s.to_sym}.reverse.each {|a| args.unshift a} + args.shift.to_s.split('.').map { |s| s.to_sym }.reverse.each { |a| args.unshift a } yield args.first, args end @@ -186,13 +189,13 @@ def restore_method!(method_name) end def restore_original_method!(method_name) - if @klass.instance_method(method_name).owner == @klass - alias_method_name = build_alias_method_name(method_name) - @klass.class_exec do - remove_method method_name - alias_method method_name, alias_method_name - remove_method alias_method_name - end + return unless @klass.instance_method(method_name).owner == @klass + + alias_method_name = build_alias_method_name(method_name) + @klass.class_exec do + remove_method method_name + alias_method method_name, alias_method_name + remove_method alias_method_name end end @@ -219,7 +222,7 @@ def observe!(method_name) if RSpec::Mocks.configuration.verify_partial_doubles? unless public_protected_or_private_method_defined?(method_name) raise MockExpectationError, - "#{@klass} does not implement ##{method_name}" + "#{@klass} does not implement ##{method_name}" end end @@ -229,14 +232,14 @@ def observe!(method_name) recorder = self @klass.__send__(:define_method, method_name) do |*args, &blk| recorder.playback!(self, method_name) - self.__send__(method_name, *args, &blk) + __send__(method_name, *args, &blk) end end def mark_invoked!(method_name) backup_method!(method_name) recorder = self - @klass.__send__(:define_method, method_name) do |*args, &blk| + @klass.__send__(:define_method, method_name) do |*_args, &_blk| invoked_instance = recorder.instance_that_received(method_name) inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>" raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{inspect} but has already been received by #{invoked_instance}" @@ -250,15 +253,14 @@ def allow_no_prepended_module_definition_of(method_name) return unless problem_mod raise RSpec::Mocks::MockExpectationError, - "Using `any_instance` to stub a method (#{method_name}) that has been " + - "defined on a prepended module (#{problem_mod}) is not supported." + "Using `any_instance` to stub a method (#{method_name}) that has been " \ + "defined on a prepended module (#{problem_mod}) is not supported." end else - def allow_no_prepended_module_definition_of(method_name) + def allow_no_prepended_module_definition_of(_method_name) # nothing to do; prepends aren't supported on this version of ruby end end - end end end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index 16071eca9..c4fa2e295 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -3,7 +3,6 @@ module Mocks module AnyInstance # @private class StubChain < Chain - # @private def expectation_fulfilled? true @@ -36,10 +35,9 @@ def invocation_order } end - def verify_invocation_order(rspec_method_name, *args, &block) - unless invocation_order[rspec_method_name].include?(last_message) - raise(NoMethodError, "Undefined method #{rspec_method_name}") - end + def verify_invocation_order(rspec_method_name, *_args, &_block) + return if invocation_order[rspec_method_name].include?(last_message) + raise NoMethodError, "Undefined method #{rspec_method_name}" end end end diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index 927e36fec..a7d15ea5e 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -46,10 +46,10 @@ def initialize(*expected_args) @expected_args = expected_args @matchers = case expected_args.first - when ArgumentMatchers::AnyArgsMatcher then Array - when ArgumentMatchers::NoArgsMatcher then [] - else expected_args - end + when ArgumentMatchers::AnyArgsMatcher then Array + when ArgumentMatchers::NoArgsMatcher then [] + else expected_args + end end # @api public diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index f5916c59f..9194e2939 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -5,7 +5,6 @@ module RSpec module Mocks - # ArgumentMatchers are placeholders that you can include in message # expectations to match arguments against a broader check than simple # equality. @@ -136,7 +135,7 @@ def description # @private class AnyArgMatcher - def ===(other) + def ===(_other) true end @@ -171,14 +170,14 @@ def initialize(expected) def ===(predicate, actual) @expected.__send__(predicate) do |k, v| - actual.has_key?(k) && Support::FuzzyMatcher.values_match?(v, actual[k]) + actual.key?(k) && Support::FuzzyMatcher.values_match?(v, actual[k]) end rescue NoMethodError false end def description(name) - "#{name}(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})" + "#{name}(#{@expected.inspect.sub(/^\{/, "").sub(/\}$/, "")})" end end @@ -226,7 +225,7 @@ def initialize(*methods_to_respond_to) end def ===(value) - @methods_to_respond_to.all? {|message| value.respond_to?(message)} + @methods_to_respond_to.all? { |message| value.respond_to?(message) } end def description diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 35595d579..6da1ce2e1 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -2,7 +2,6 @@ module RSpec module Mocks # Provides configuration options for rspec-mocks. class Configuration - def initialize @yield_receiver_to_any_instance_implementation_blocks = true @verify_doubled_constant_names = false @@ -26,9 +25,7 @@ def yield_receiver_to_any_instance_implementation_blocks? # mocks.yield_receiver_to_any_instance_implementation_blocks = false # end # end - def yield_receiver_to_any_instance_implementation_blocks=(arg) - @yield_receiver_to_any_instance_implementation_blocks = arg - end + attr_writer :yield_receiver_to_any_instance_implementation_blocks # Adds `stub` and `should_receive` to the given # modules or classes. This is usually only necessary @@ -105,9 +102,7 @@ def verify_doubled_constant_names? # constant. You probably only want to set this when running your entire # test suite, with all production code loaded. Setting this for an # isolated unit test will prevent you from being able to isolate it! - def verify_doubled_constant_names=(val) - @verify_doubled_constant_names = val - end + attr_writer :verify_doubled_constant_names def transfer_nested_constants? !!@transfer_nested_constants @@ -115,9 +110,7 @@ def transfer_nested_constants? # Sets the default for the `transfer_nested_constants` option when # stubbing constants. - def transfer_nested_constants=(val) - @transfer_nested_constants = val - end + attr_writer :transfer_nested_constants # When set to true, partial mocks will be verified the same as object # doubles. Any stubs will have their arguments checked against the original diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 6db832673..b9260f33e 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -40,29 +40,38 @@ def raise_unexpected_message_error(message, *args) def raise_unexpected_message_args_error(expectation, *args) expected_args = format_args(*expectation.expected_args) actual_args = format_received_args(*args) - __raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}" + __raise "#{intro} received #{expectation.message.inspect} with " \ + "unexpected arguments\n expected: #{expected_args}\n" \ + " got: #{actual_args}" end # @private def raise_missing_default_stub_error(expectation, *args) expected_args = format_args(*expectation.expected_args) actual_args = format_received_args(*args) - __raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}\n Please stub a default value first if message might be received with other args as well. \n" + __raise "#{intro} received #{expectation.message.inspect} with " \ + "unexpected arguments\n expected: #{expected_args}\n" \ + " got: #{actual_args}\n Please stub a default value " \ + "first if message might be received with other args as well. \n" end # @private def raise_similar_message_args_error(expectation, *args_for_multiple_calls) expected_args = format_args(*expectation.expected_args) - actual_args = args_for_multiple_calls.collect {|a| format_received_args(*a)}.join(", ") - __raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}" + actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ") + __raise "#{intro} received #{expectation.message.inspect} with " \ + "unexpected arguments\n expected: #{expected_args}\n" \ + " got: #{actual_args}" end + # rubocop:disable Style/ParameterLists # @private def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, *args) expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) received_part = received_part_of_expectation_error(actual_received_count, *args) __raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}" end + # rubocop:enable Style/ParameterLists # @private def raise_unimplemented_error(doubled_module, method_name) @@ -87,10 +96,10 @@ def raise_invalid_arguments_error(verifier) # @private def raise_expired_test_double_error raise ExpiredTestDoubleError, - "#{intro} was originally created in one example but has leaked into " + - "another example and can no longer be used. rspec-mocks' doubles are " + - "designed to only last for one example, and you need to create a new " + - "one in each example you wish to use it for." + "#{intro} was originally created in one example but has leaked into " \ + "another example and can no longer be used. rspec-mocks' doubles are " \ + "designed to only last for one example, and you need to create a new " \ + "one in each example you wish to use it for." end # @private @@ -128,15 +137,13 @@ def expected_method_call_args_description(args) # @private def method_call_args_description(args) case args.first - when ArgumentMatchers::AnyArgsMatcher - return " with any arguments" - when ArgumentMatchers::NoArgsMatcher - return " with no arguments" + when ArgumentMatchers::AnyArgsMatcher then " with any arguments" + when ArgumentMatchers::NoArgsMatcher then " with no arguments" end end # @private - def describe_expectation(message, expected_received_count, actual_received_count, *args) + def describe_expectation(message, expected_received_count, _actual_received_count, *args) "have received #{message}#{format_args(*args)} #{count_message(expected_received_count)}" end @@ -162,30 +169,30 @@ def raise_wrong_arity_error(args_to_yield, signature) # @private def raise_only_valid_on_a_partial_double(method) - __raise "#{intro} is a pure test double. `#{method}` is only " + + __raise "#{intro} is a pure test double. `#{method}` is only " \ "available on a partial double." end # @private def raise_expectation_on_unstubbed_method(method) - __raise "#{intro} expected to have received #{method}, but that " + + __raise "#{intro} expected to have received #{method}, but that " \ "object is not a spy or method has not been stubbed." end # @private def raise_expectation_on_mocked_method(method) - __raise "#{intro} expected to have received #{method}, but that " + + __raise "#{intro} expected to have received #{method}, but that " \ "method has been mocked instead of stubbed or spied." end def self.raise_double_negation_error(wrapped_expression) - raise "Isn't life confusing enough? You've already set a " + - "negative message expectation and now you are trying to " + - "negate it again with `never`. What does an expression like " + + raise "Isn't life confusing enough? You've already set a " \ + "negative message expectation and now you are trying to " \ + "negate it again with `never`. What does an expression like " \ "`#{wrapped_expression}.not_to receive(:msg).never` even mean?" end - private + private def intro if @name @@ -203,7 +210,7 @@ def intro def __raise(message) message = opts[:message] unless opts[:message].nil? - Kernel::raise(RSpec::Mocks::MockExpectationError, message) + Kernel.raise(RSpec::Mocks::MockExpectationError, message) end def arg_message(*args) @@ -215,7 +222,7 @@ def format_args(*args) end def arg_list(*args) - args.collect {|arg| arg_has_valid_description(arg) ? arg.description : arg.inspect }.join(", ") + args.map { |arg| arg_has_valid_description(arg) ? arg.description : arg.inspect }.join(", ") end def arg_has_valid_description(arg) @@ -229,19 +236,18 @@ def format_received_args(*args) end def received_arg_list(*args) - args.collect(&:inspect).join(", ") + args.map(&:inspect).join(", ") end def count_message(count, expectation_count_type=nil) return "at least #{times(count.abs)}" if count < 0 || expectation_count_type == :at_least return "at most #{times(count)}" if expectation_count_type == :at_most - return times(count) + times(count) end def times(count) "#{count} time#{count == 1 ? '' : 's'}" end - end end end diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 1957c1200..d994b5cd5 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -197,7 +197,7 @@ def allow_message_expectations_on_nil # stub_const("CardDeck", Class.new, :transfer_nested_constants => [:SUITS]) # CardDeck::SUITS # => our suits array # CardDeck::NUM_CARDS # => uninitialized constant error - def stub_const(constant_name, value, options = {}) + def stub_const(constant_name, value, options={}) ConstantMutator.stub(constant_name, value, options) end @@ -356,9 +356,9 @@ def self.declare_verifying_double(type, ref, *args) !ref.defined? raise VerifyingDoubleNotDefinedError, - "#{ref.description} is not a defined constant. " + - "Perhaps you misspelt it? " + - "Disable check with verify_doubled_constant_names configuration option." + "#{ref.description} is not a defined constant. " \ + "Perhaps you misspelt it? " \ + "Disable check with verify_doubled_constant_names configuration option." end declare_double(type, ref, *args) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index b7e6446d3..44145ca08 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -53,7 +53,7 @@ def method_is_stashed? # @private def stash - return if !method_defined_directly_on_klass? + return unless method_defined_directly_on_klass? @original_method ||= ::RSpec::Support.method_handle_for(@object, @method) end @@ -80,9 +80,9 @@ def handle_restoration_failures yield rescue TypeError RSpec.warn_with( - "RSpec failed to properly restore a partial double (#{@object.inspect}) " + - "to its original state due to a known bug in MRI 2.0.0-p195 & p247 " + - "(https://bugs.ruby-lang.org/issues/8686). This object may remain " + + "RSpec failed to properly restore a partial double (#{@object.inspect}) " \ + "to its original state due to a known bug in MRI 2.0.0-p195 & p247 " \ + "(https://bugs.ruby-lang.org/issues/8686). This object may remain " \ "screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.", :call_site => nil, :use_spec_location_as_call_site => true ) @@ -102,7 +102,7 @@ def method_defined_directly_on_klass? end # @private - def method_defined_on_klass?(klass = @klass) + def method_defined_on_klass?(klass=@klass) MethodReference.method_defined_at_any_visibility?(klass, @method) end diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 10a836a27..3fcfe7090 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -3,9 +3,9 @@ module Mocks module Matchers # @private class HaveReceived - COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice thrice) - ARGS_CONSTRAINTS = %w(with) - CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w(ordered) + COUNT_CONSTRAINTS = %w[exactly at_least at_most times once twice thrice] + ARGS_CONSTRAINTS = %w[with] + CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w[ordered] def initialize(method_name, &block) @method_name = method_name @@ -71,14 +71,13 @@ def apply_constraints_to(expectation) end def ensure_count_unconstrained - if count_constraint - raise RSpec::Mocks::MockExpectationError, - "can't use #{count_constraint} when negative" - end + return unless count_constraint + raise RSpec::Mocks::MockExpectationError, + "can't use #{count_constraint} when negative" end def count_constraint - @constraints.map(&:first).detect do |constraint| + @constraints.map(&:first).find do |constraint| COUNT_CONSTRAINTS.include?(constraint) end end diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index e5ff3941f..91760c268 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -61,14 +61,14 @@ def setup_any_instance_allowance(subject, &block) private def warn_if_any_instance(expression, subject) - if AnyInstance::Proxy === subject - RSpec.warning( - "`#{expression}(#{subject.klass}.any_instance).to` " << - "is probably not what you meant, it does not operate on " << - "any instance of `#{subject.klass}`. " << - "Use `#{expression}_any_instance_of(#{subject.klass}).to` instead." - ) - end + return unless AnyInstance::Proxy === subject + + RSpec.warning( + "`#{expression}(#{subject.klass}.any_instance).to` " \ + "is probably not what you meant, it does not operate on " \ + "any instance of `#{subject.klass}`. " \ + "Use `#{expression}_any_instance_of(#{subject.klass}).to` instead." + ) end def setup_mock_proxy_method_substitute(subject, method, block) diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index d45e493e9..268a54ded 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -44,11 +44,10 @@ def setup_expectation(subject, &block) replay_customizations(chain) end - def setup_negative_expectation(*args) - raise NegationUnsupportedError.new( - "`expect(...).not_to receive_message_chain` is not supported " + - "since it doesn't really make sense. What would it even mean?" - ) + def setup_negative_expectation(*_args) + raise NegationUnsupportedError, + "`expect(...).not_to receive_message_chain` is not supported " \ + "since it doesn't really make sense. What would it even mean?" end alias matches? setup_expectation diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index 1fcb03d04..96962882a 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -3,7 +3,6 @@ module Mocks module Matchers # @private class ReceiveMessages - def initialize(message_return_value_hash) @message_return_value_hash = message_return_value_hash @backtrace_line = CallerFilter.first_non_rspec_line @@ -15,29 +14,29 @@ def name def setup_expectation(subject) warn_about_block if block_given? - each_message_on( proxy_on(subject) ) do |host, message, return_value| + each_message_on(proxy_on(subject)) do |host, message, return_value| host.add_simple_expectation(message, return_value, @backtrace_line) end end alias matches? setup_expectation - def setup_negative_expectation(subject) + def setup_negative_expectation(_subject) raise NegationUnsupportedError, - "`expect(...).to_not receive_messages` is not supported since it " + - "doesn't really make sense. What would it even mean?" + "`expect(...).to_not receive_messages` is not supported since it " \ + "doesn't really make sense. What would it even mean?" end alias does_not_match? setup_negative_expectation def setup_allowance(subject) warn_about_block if block_given? - each_message_on( proxy_on(subject) ) do |host, message, return_value| + each_message_on(proxy_on(subject)) do |host, message, return_value| host.add_simple_stub(message, return_value) end end def setup_any_instance_expectation(subject) warn_about_block if block_given? - each_message_on( any_instance_of(subject) ) do |host, message, return_value| + each_message_on(any_instance_of(subject)) do |host, message, return_value| host.should_receive(message).and_return(return_value) end end diff --git a/lib/rspec/mocks/message_chain.rb b/lib/rspec/mocks/message_chain.rb index f6fc44d61..4106cb2ec 100644 --- a/lib/rspec/mocks/message_chain.rb +++ b/lib/rspec/mocks/message_chain.rb @@ -12,10 +12,10 @@ def initialize(object, *chain, &blk) # @api private def setup_chain if chain.length > 1 - if matching_stub = find_matching_stub + if (matching_stub = find_matching_stub) chain.shift chain_on(matching_stub.invoke(nil), *chain, &@block) - elsif matching_expectation = find_matching_expectation + elsif (matching_expectation = find_matching_expectation) chain.shift chain_on(matching_expectation.invoke_without_incrementing_received_count(nil), *chain, &@block) else @@ -30,8 +30,8 @@ def setup_chain private - def expectation(object, message, &return_block) - raise NotImplementedError.new + def expectation(_object, _message, &_return_block) + raise NotImplementedError end def chain_on(object, *chain, &block) @@ -42,7 +42,7 @@ def chain_on(object, *chain, &block) def format_chain(*chain, &blk) if Hash === chain.last hash = chain.pop - hash.each do |k,v| + hash.each do |k, v| chain << k blk = Proc.new { v } end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 35403027a..3d095a686 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -1,6 +1,5 @@ module RSpec module Mocks - # A message expectation that only allows concrete return values to be set # for a message. While this same effect can be achieved using a standard # MessageExpecation, this version is much faster and so can be used as an @@ -8,8 +7,7 @@ module Mocks # # @private class SimpleMessageExpectation - - def initialize(message, response, error_generator, backtrace_line = nil) + def initialize(message, response, error_generator, backtrace_line=nil) @message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line @received = false end @@ -45,6 +43,7 @@ class MessageExpectation attr_writer :expected_received_count, :expected_from, :argument_list_matcher protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation= + # rubocop:disable Style/ParameterLists # @private def initialize(error_generator, expectation_ordering, expected_from, method_double, type=:expectation, opts={}, &implementation_block) @@ -70,6 +69,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @implementation = Implementation.new self.inner_implementation_action = implementation_block end + # rubocop:enable Style/ParameterLists # @private def expected_args @@ -110,7 +110,7 @@ def and_return(first_value, *values) end values.unshift(first_value) - @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least) + @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least) self.terminal_implementation_action = AndReturnImplementation.new(values) nil @@ -186,7 +186,7 @@ def and_wrap_original(&block) # allow(car).to receive(:go).and_raise(OutOfGas) # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive") # allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz)) - def and_raise(exception = RuntimeError, message = nil) + def and_raise(exception=RuntimeError, message=nil) if exception.respond_to?(:exception) exception = message ? exception.exception(message) : exception.exception end @@ -253,7 +253,7 @@ def called_max_times? # @private def matches_name_but_not_args(message, *args) - @message == message and not @argument_list_matcher.args_match?(*args) + @message == message && !@argument_list_matcher.args_match?(*args) end # @private @@ -315,7 +315,7 @@ def generate_error def expectation_count_type return :at_least if @at_least return :at_most if @at_most - return nil + nil end # @private @@ -354,7 +354,7 @@ def raise_out_of_order_error def with(*args, &block) if args.empty? raise ArgumentError, - "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments." + "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments." end self.inner_implementation_action = block @@ -482,7 +482,6 @@ def additional_expected_calls @expected_received_count - 1 end - # @private def ordered? @ordered @@ -506,15 +505,13 @@ def increase_actual_received_count! private def invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block) - if yield_receiver_to_implementation_block? - args.unshift(orig_object) - end + args.unshift(orig_object) if yield_receiver_to_implementation_block? if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count)) @actual_received_count += increment @failed_fast = true - #args are the args we actually received, @argument_list_matcher is the - #list of args we were expecting + # args are the args we actually received, @argument_list_matcher is the + # list of args we were expecting @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args) end @@ -563,7 +560,7 @@ def terminal_implementation_action=(action) def warn_about_stub_override RSpec.warning( - "You're overriding a previous stub implementation of `#{@message}`. " + + "You're overriding a previous stub implementation of `#{@message}`. " \ "Called from #{CallerFilter.first_non_rspec_line}." ) end @@ -578,7 +575,7 @@ def initialize(args_to_yield, eval_context, error_generator) @error_generator = error_generator end - def call(*args_to_ignore, &block) + def call(*_args_to_ignore, &block) return if @args_to_yield.empty? && @eval_context.nil? @error_generator.raise_missing_block_error @args_to_yield unless block @@ -603,7 +600,7 @@ def initialize(values_to_return) @values_to_return = values_to_return end - def call(*args_to_ignore, &block) + def call(*_args_to_ignore, &_block) if @values_to_return.size > 1 @values_to_return.shift else @@ -645,15 +642,15 @@ def initialize(method, block) CannotModifyFurtherError = Class.new(StandardError) - def initial_action=(value) + def initial_action=(_value) raise cannot_modify_further_error end - def inner_action=(value) + def inner_action=(_value) raise cannot_modify_further_error end - def terminal_action=(value) + def terminal_action=(_value) raise cannot_modify_further_error end @@ -672,7 +669,7 @@ def call(*args, &block) private def cannot_modify_further_error - CannotModifyFurtherError.new "This method has already been configured " + + CannotModifyFurtherError.new "This method has already been configured " \ "to call the original implementation, and cannot be modified further." end end @@ -685,9 +682,8 @@ def self.line(location) yield rescue RSpec::Mocks::MockExpectationError => error error.backtrace.insert(0, location) - Kernel::raise error + Kernel.raise error end end - end end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 000e57797..34b84aafd 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -59,7 +59,7 @@ def define_proxy_method define_method(method_name) do |*args, &block| method_double.proxy_method_invoked(self, *args, &block) end - self.__send__ visibility, method_name + __send__(visibility, method_name) end @method_is_proxied = true @@ -69,7 +69,7 @@ def define_proxy_method # method to perform additional operations. # # @private - def proxy_method_invoked(obj, *args, &block) + def proxy_method_invoked(_obj, *args, &block) @proxy.message_received method_name, *args, &block end @@ -80,9 +80,7 @@ def restore_original_method definition_target.__send__(:remove_method, @method_name) - if @method_stasher.method_is_stashed? - @method_stasher.restore - end + @method_stasher.restore if @method_stasher.method_is_stashed? restore_original_visibility @method_is_proxied = false @@ -91,7 +89,9 @@ def restore_original_method # @private def show_frozen_warning RSpec.warn_with( - "WARNING: rspec-mocks was unable to restore the original `#{@method_name}` method on #{@object.inspect} because it has been frozen. If you reuse this object, `#{@method_name}` will continue to respond with its stub implementation.", + "WARNING: rspec-mocks was unable to restore the original `#{@method_name}` " \ + "method on #{@object.inspect} because it has been frozen. If you reuse this " \ + "object, `#{@method_name}` will continue to respond with its stub implementation.", :call_site => nil, :use_spec_location_as_call_site => true ) @@ -107,7 +107,7 @@ def restore_original_visibility # @private def verify - expectations.each {|e| e.verify_messages_received} + expectations.each { |e| e.verify_messages_received } end # @private @@ -134,7 +134,7 @@ def message_expectation_class def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation) configure_method expectation = message_expectation_class.new(error_generator, expectation_ordering, - expected_from, self, :expectation, opts, &implementation) + expected_from, self, :expectation, opts, &implementation) expectations << expectation expectation end @@ -149,7 +149,7 @@ def build_expectation(error_generator, expectation_ordering) def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation) configure_method stub = message_expectation_class.new(error_generator, expectation_ordering, expected_from, - self, :stub, opts, &implementation) + self, :stub, opts, &implementation) stubs.unshift stub stub end @@ -172,7 +172,7 @@ def add_simple_expectation(method_name, response, error_generator, backtrace_lin end # @private - def setup_simple_method_double(method_name, response, collection, error_generator = nil, backtrace_line = nil) + def setup_simple_method_double(method_name, response, collection, error_generator=nil, backtrace_line=nil) define_proxy_method me = SimpleMessageExpectation.new(method_name, response, error_generator, backtrace_line) @@ -202,8 +202,6 @@ def raise_method_not_stubbed_error raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" end - private - # In Ruby 2.0.0 and above prepend will alter the method lookup chain. # We use an object's singleton class to define method doubles upon, # however if the object has had it's singleton class (as opposed to @@ -216,6 +214,9 @@ def raise_method_not_stubbed_error # of our own. # if Support::RubyFeatures.module_prepends_supported? + + private + # We subclass `Module` in order to be able to easily detect our prepended module. RSpecPrependedModule = Class.new(Module) @@ -247,6 +248,8 @@ def new_rspec_prepended_module else + private + def definition_target object_singleton_class end diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index cfc1f86f5..21896bf07 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -27,7 +27,7 @@ def implemented? # cases when we don't know if a method is implemented and # both `implemented?` and `unimplemented?` will return false. def unimplemented? - @object_reference.when_loaded do |m| + @object_reference.when_loaded do |_m| return !implemented? end @@ -44,9 +44,8 @@ def defined? end def with_signature - if original = original_method - yield Support::MethodSignature.new(original) - end + return unless (original = original_method) + yield Support::MethodSignature.new(original) end def visibility @@ -59,7 +58,7 @@ def visibility :public end - private + private def original_method @object_reference.when_loaded do |m| @@ -98,7 +97,8 @@ def self.method_visibility_for(object, method_name) # @private class InstanceMethodReference < MethodReference - private + private + def method_implemented?(mod) MethodReference.method_defined_at_any_visibility?(mod, @method_name) end @@ -133,7 +133,8 @@ def visibility_from(mod) # @private class ObjectMethodReference < MethodReference - private + private + def method_implemented?(object) object.respond_to?(@method_name, true) end diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index 109f23f10..cdbade080 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -93,12 +93,12 @@ class ConstantMutator # examples. This is an alternate public API that is provided # so you can stub constants in other contexts (e.g. helper # classes). - def self.stub(constant_name, value, options = {}) + def self.stub(constant_name, value, options={}) mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const) - DefinedConstantReplacer - else - UndefinedConstantSetter - end + DefinedConstantReplacer + else + UndefinedConstantSetter + end mutate(mutator.new(constant_name, value, options[:transfer_nested_constants])) value @@ -114,7 +114,7 @@ def self.stub(constant_name, value, options = {}) # so you can hide constants in other contexts (e.g. helper # classes). def self.hide(constant_name) - mutate(ConstantHider.new(constant_name, nil, { })) + mutate(ConstantHider.new(constant_name, nil, {})) nil end @@ -153,7 +153,7 @@ def idempotently_reset # @private class ConstantHider < BaseMutator def mutate - return unless @defined = recursive_const_defined?(full_constant_name) + return unless (@defined = recursive_const_defined?(full_constant_name)) @context = recursive_const_get(@context_parts.join('::')) @original_value = get_const_defined_on(@context, @const_name) @@ -224,12 +224,12 @@ def verify_constants_to_transfer! return [] unless should_transfer_nested_constants? { @original_value => "the original value", @mutated_value => "the stubbed value" }.each do |value, description| - unless value.respond_to?(:constants) - raise ArgumentError, - "Cannot transfer nested constants for #{@full_constant_name} " + - "since #{description} is not a class or module and only classes " + - "and modules support nested constants." - end + next if value.respond_to?(:constants) + + raise ArgumentError, + "Cannot transfer nested constants for #{@full_constant_name} " \ + "since #{description} is not a class or module and only classes " \ + "and modules support nested constants." end if Array === @transfer_nested_constants @@ -239,9 +239,9 @@ def verify_constants_to_transfer! if undefined_constants.any? available_constants = constants_defined_on(@original_value) - @transfer_nested_constants raise ArgumentError, - "Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " + - "for #{@full_constant_name} since they are not defined. Did you mean " + - "#{available_constants.join(' or ')}?" + "Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " \ + "for #{@full_constant_name} since they are not defined. Did you mean " \ + "#{available_constants.join(' or ')}?" end @transfer_nested_constants @@ -289,10 +289,10 @@ def reset def name_for(parent, name) root = if parent == Object - '' - else - parent.name - end + '' + else + parent.name + end root + '::' + name end end @@ -315,7 +315,7 @@ def self.mutate(mutator) # @api private def self.raise_on_invalid_const lambda do |const_name, failed_name| - raise "Cannot stub constant #{failed_name} on #{const_name} " + + raise "Cannot stub constant #{failed_name} on #{const_name} " \ "since #{const_name} is not a module." end end diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 9ccda42bc..fce87d166 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -1,21 +1,20 @@ module RSpec module Mocks - # @private class ObjectReference # Returns an appropriate Object or Module reference based # on the given argument. - def self.for(object_module_or_name, allow_direct_object_refs = false) + def self.for(object_module_or_name, allow_direct_object_refs=false) case object_module_or_name - when Module then DirectModuleReference.new(object_module_or_name) - when String then NamedObjectReference.new(object_module_or_name) + when Module then DirectModuleReference.new(object_module_or_name) + when String then NamedObjectReference.new(object_module_or_name) + else + if allow_direct_object_refs + DirectObjectReference.new(object_module_or_name) else - if allow_direct_object_refs - DirectObjectReference.new(object_module_or_name) - else - raise ArgumentError, - "Module or String expected, got #{object_module_or_name.inspect}" - end + raise ArgumentError, + "Module or String expected, got #{object_module_or_name.inspect}" + end end end end @@ -35,7 +34,7 @@ def description def const_to_replace raise ArgumentError, - "Can not perform constant replacement with an object." + "Can not perform constant replacement with an object." end def defined? @@ -78,11 +77,11 @@ def const_to_replace end alias description const_to_replace - def when_loaded(&block) + def when_loaded(&_block) yield object if object end - private + private def object @object ||= Constant.original(@const_name).original_value diff --git a/lib/rspec/mocks/order_group.rb b/lib/rspec/mocks/order_group.rb index 52f882ed9..a9947995c 100644 --- a/lib/rspec/mocks/order_group.rb +++ b/lib/rspec/mocks/order_group.rb @@ -25,10 +25,10 @@ def ready_for?(expectation) # @private def consume remaining_expectations.each_with_index do |expectation, index| - if expectation.ordered? - @index += index + 1 - return expectation - end + next unless expectation.ordered? + + @index += index + 1 + return expectation end nil end @@ -76,7 +76,6 @@ def expected_invocations def expectation_for(message) @expectations.find { |e| message == e } end - end end end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 11074f580..7b7110028 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -9,7 +9,7 @@ def ==(expectation) end # @private - def ensure_implemented(*args) + def ensure_implemented(*_args) # noop for basic proxies, see VerifyingProxy for behaviour. end @@ -42,7 +42,7 @@ def as_null_object end # @private - def original_method_handle_for(message) + def original_method_handle_for(_message) nil end @@ -89,20 +89,19 @@ def replay_received_message_on(expectation, &block) end @messages_received.each do |(actual_method_name, args, _)| - if expectation.matches?(actual_method_name, *args) - expectation.invoke(nil) - block.call(*args) if block - end - end + next unless expectation.matches?(actual_method_name, *args) + expectation.invoke(nil) + block.call(*args) if block + end end # @private def check_for_unexpected_arguments(expectation) @messages_received.each do |(method_name, args, _)| - if expectation.matches_name_but_not_args(method_name, *args) - raise_unexpected_message_args_error(expectation, *args) - end + next unless expectation.matches_name_but_not_args(method_name, *args) + + raise_unexpected_message_args_error(expectation, *args) end end @@ -129,7 +128,7 @@ def remove_stub_if_present(method_name) # @private def verify - @method_doubles.each_value {|d| d.verify} + @method_doubles.each_value { |d| d.verify } end # @private @@ -139,12 +138,12 @@ def reset # @private def received_message?(method_name, *args, &block) - @messages_received.any? {|array| array == [method_name, args, block]} + @messages_received.any? { |array| array == [method_name, args, block] } end # @private def has_negative_expectation?(message) - method_double_for(message).expectations.detect {|expectation| expectation.negative_expectation_for?(message)} + method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) } end # @private @@ -162,16 +161,19 @@ def message_received(message, *args, &block) if (stub && expectation && expectation.called_max_times?) || (stub && !expectation) expectation.increase_actual_received_count! if expectation && expectation.actual_received_count_matters? - if expectation = find_almost_matching_expectation(message, *args) + if (expectation = find_almost_matching_expectation(message, *args)) expectation.advise(*args) unless expectation.expected_messages_received? end stub.invoke(nil, *args, &block) elsif expectation expectation.invoke(stub, *args, &block) - elsif expectation = find_almost_matching_expectation(message, *args) + elsif (expectation = find_almost_matching_expectation(message, *args)) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? - raise_unexpected_message_args_error(expectation, *args) unless (has_negative_expectation?(message) or null_object?) - elsif stub = find_almost_matching_stub(message, *args) + + if null_object? || !has_negative_expectation?(message) + raise_unexpected_message_args_error(expectation, *args) + end + elsif (stub = find_almost_matching_stub(message, *args)) stub.advise(*args) raise_missing_default_stub_error(stub, *args) elsif Class === @object @@ -197,7 +199,7 @@ def raise_missing_default_stub_error(expectation, *args) end # @private - def visibility_for(method_name) + def visibility_for(_method_name) # This is the default (for test doubles). Subclasses override this. :public end @@ -244,11 +246,11 @@ def find_best_matching_expectation_for(method_name) end def find_matching_method_stub(method_name, *args) - method_double_for(method_name).stubs.find {|stub| stub.matches?(method_name, *args)} + method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) } end def find_almost_matching_stub(method_name, *args) - method_double_for(method_name).stubs.find {|stub| stub.matches_name_but_not_args(method_name, *args)} + method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) } end end @@ -295,7 +297,7 @@ def visibility_for(method_name) end def reset - @method_doubles.each_value {|d| d.reset} + @method_doubles.each_value { |d| d.reset } super end @@ -415,7 +417,7 @@ def add_stub(method_name, opts={}, &implementation) private - def warn method_name + def warn(method_name) source = CallerFilter.first_non_rspec_line Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.") end diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 73ceae9eb..2ed35ebc3 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -5,23 +5,23 @@ module Mocks # the scope of an example. Called "root" because it serves # as the root of the space stack. class RootSpace - def proxy_for(*args) + def proxy_for(*_args) raise_lifecycle_message end - def any_instance_recorder_for(*args) + def any_instance_recorder_for(*_args) raise_lifecycle_message end - def any_instance_proxy_for(*args) + def any_instance_proxy_for(*_args) raise_lifecycle_message end - def register_constant_mutator(mutator) + def register_constant_mutator(_mutator) raise_lifecycle_message end - def any_instance_recorders_from_ancestry_of(object) + def any_instance_recorders_from_ancestry_of(_object) raise_lifecycle_message end @@ -31,7 +31,7 @@ def reset_all def verify_all end - def registered?(object) + def registered?(_object) false end @@ -43,7 +43,7 @@ def new_scope def raise_lifecycle_message raise OutsideOfExampleError, - "The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported." + "The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported." end end @@ -84,7 +84,7 @@ def constant_mutator_for(name) @constant_mutators.find { |m| m.full_constant_name == name } end - def any_instance_recorder_for(klass, only_return_existing = false) + def any_instance_recorder_for(klass, only_return_existing=false) any_instance_mutex.synchronize do id = klass.__id__ any_instance_recorders.fetch(id) do @@ -112,7 +112,7 @@ def proxy_for(object) alias ensure_registered proxy_for def registered?(object) - proxies.has_key?(id_for object) + proxies.key?(id_for object) end def any_instance_recorders_from_ancestry_of(object) @@ -147,21 +147,21 @@ def self.synchronize def proxy_not_found_for(id, object) proxies[id] = case object - when NilClass then ProxyForNil.new(@expectation_ordering) - when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering) - when Class - if RSpec::Mocks.configuration.verify_partial_doubles? - VerifyingPartialClassDoubleProxy.new(self, object, @expectation_ordering) - else - PartialClassDoubleProxy.new(self, object, @expectation_ordering) - end - else - if RSpec::Mocks.configuration.verify_partial_doubles? - VerifyingPartialDoubleProxy.new(object, @expectation_ordering) - else - PartialDoubleProxy.new(object, @expectation_ordering) - end - end + when NilClass then ProxyForNil.new(@expectation_ordering) + when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering) + when Class + if RSpec::Mocks.configuration.verify_partial_doubles? + VerifyingPartialClassDoubleProxy.new(self, object, @expectation_ordering) + else + PartialClassDoubleProxy.new(self, object, @expectation_ordering) + end + else + if RSpec::Mocks.configuration.verify_partial_doubles? + VerifyingPartialDoubleProxy.new(object, @expectation_ordering) + else + PartialDoubleProxy.new(object, @expectation_ordering) + end + end end def any_instance_recorder_not_found_for(id, klass) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 1b5af1211..a2a28b70f 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -10,7 +10,7 @@ def self.warn_about_should! end # @private - def self.warn_unless_should_configured(method_name ,replacement = "the new `:expect` syntax or explicitly enable `:should`") + def self.warn_unless_should_configured(method_name , replacement="the new `:expect` syntax or explicitly enable `:should`") if @warn_about_should RSpec.deprecate( "Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax", @@ -23,7 +23,7 @@ def self.warn_unless_should_configured(method_name ,replacement = "the new `:exp # @api private # Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc). - def self.enable_should(syntax_host = default_should_syntax_host) + def self.enable_should(syntax_host=default_should_syntax_host) @warn_about_should = false if syntax_host == default_should_syntax_host return if should_enabled?(syntax_host) @@ -41,7 +41,7 @@ def should_not_receive(message, &block) def stub(message_or_hash, opts={}, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) if ::Hash === message_or_hash - message_or_hash.each {|message, value| stub(message).and_return value } + message_or_hash.each { |message, value| stub(message).and_return value } else ::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block) end @@ -86,7 +86,7 @@ def any_instance # @api private # Disables the should syntax (`dbl.stub`, `dbl.should_receive`, etc). - def self.disable_should(syntax_host = default_should_syntax_host) + def self.disable_should(syntax_host=default_should_syntax_host) return unless should_enabled?(syntax_host) syntax_host.class_exec do @@ -107,7 +107,7 @@ def self.disable_should(syntax_host = default_should_syntax_host) # @api private # Enables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc). - def self.enable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods) + def self.enable_expect(syntax_host=::RSpec::Mocks::ExampleMethods) return if expect_enabled?(syntax_host) syntax_host.class_exec do @@ -147,7 +147,7 @@ def expect(target) # @api private # Disables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc). - def self.disable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods) + def self.disable_expect(syntax_host=::RSpec::Mocks::ExampleMethods) return unless expect_enabled?(syntax_host) syntax_host.class_exec do @@ -166,13 +166,13 @@ def self.disable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods) # @api private # Indicates whether or not the should syntax is enabled. - def self.should_enabled?(syntax_host = default_should_syntax_host) + def self.should_enabled?(syntax_host=default_should_syntax_host) syntax_host.method_defined?(:should_receive) end # @api private # Indicates whether or not the expect syntax is enabled. - def self.expect_enabled?(syntax_host = ::RSpec::Mocks::ExampleMethods) + def self.expect_enabled?(syntax_host=::RSpec::Mocks::ExampleMethods) syntax_host.method_defined?(:allow) end diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 557977228..43436ccd3 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -15,7 +15,7 @@ def self.delegate_to(matcher_method) end end - def self.delegate_not_to(matcher_method, options = {}) + def self.delegate_not_to(matcher_method, options={}) method_name = options.fetch(:from) define_method(method_name) do |matcher, &block| case matcher @@ -30,7 +30,7 @@ def self.delegate_not_to(matcher_method, options = {}) end def self.disallow_negation(method_name) - define_method(method_name) do |matcher, *args| + define_method(method_name) do |matcher, *_args| raise_negation_unsupported(method_name, matcher) end end @@ -47,14 +47,14 @@ def define_matcher(matcher, name, &block) def raise_unsupported_matcher(method_name, matcher) raise UnsupportedMatcherError, - "only the `receive` or `receive_messages` matchers are supported " + - "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" + "only the `receive` or `receive_messages` matchers are supported " \ + "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" end def raise_negation_unsupported(method_name, matcher) raise NegationUnsupportedError, - "`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " + - "doesn't really make sense. What would it even mean?" + "`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " \ + "doesn't really make sense. What would it even mean?" end def expression diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 5ff3f6ae1..3b2056ded 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -39,12 +39,12 @@ def ==(other) # @private def inspect - "#<#{self.class}:#{sprintf '0x%x', self.object_id} @name=#{@name.inspect}>" + "#<#{self.class}:#{'0x%x' % object_id} @name=#{@name.inspect}>" end # @private def to_s - inspect.gsub('<','[').gsub('>',']') + inspect.gsub('<', '[').gsub('>', ']') end # @private @@ -54,7 +54,7 @@ def respond_to?(message, incl_private=false) # @private def __build_mock_proxy_unless_expired(order_group) - __raise_expired_error or __build_mock_proxy(order_group) + __raise_expired_error || __build_mock_proxy(order_group) end # @private diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index ca1e1ad20..f975046b1 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -3,7 +3,6 @@ module RSpec module Mocks - # @private module VerifyingDouble def respond_to?(message, include_private=false) @@ -11,11 +10,11 @@ def respond_to?(message, include_private=false) method_ref = __mock_proxy.method_reference[message] - return case method_ref.visibility - when :public then true - when :private then include_private - when :protected then include_private || RUBY_VERSION.to_f < 2.0 - else !method_ref.unimplemented? + case method_ref.visibility + when :public then true + when :private then include_private + when :protected then include_private || RUBY_VERSION.to_f < 2.0 + else !method_ref.unimplemented? end end @@ -74,8 +73,8 @@ def initialize(doubled_module, *args) def __build_mock_proxy(order_group) VerifyingProxy.new(self, order_group, @name, - @doubled_module, - InstanceMethodReference + @doubled_module, + InstanceMethodReference ) end end @@ -88,7 +87,7 @@ module ObjectVerifyingDoubleMethods include TestDouble include VerifyingDouble - def as_stubbed_const(options = {}) + def as_stubbed_const(options={}) ConstantMutator.stub(@doubled_module.const_to_replace, self, options) self end @@ -102,8 +101,8 @@ def initialize(doubled_module, *args) def __build_mock_proxy(order_group) VerifyingProxy.new(self, order_group, @name, - @doubled_module, - ObjectMethodReference + @doubled_module, + ObjectMethodReference ) end end @@ -124,6 +123,5 @@ class ObjectVerifyingDouble class ClassVerifyingDouble < Module include ObjectVerifyingDoubleMethods end - end end diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index ea20e3048..6acdf8090 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -2,13 +2,11 @@ module RSpec module Mocks - # A message expectation that knows about the real implementation of the # message being expected, so that it can verify that any expectations # have the valid arguments. # @api private class VerifyingMessageExpectation < MessageExpectation - # A level of indirection is used here rather than just passing in the # method itself, since method look up is expensive and we only want to # do it if actually needed. @@ -27,13 +25,13 @@ def initialize(*args) def with(*args, &block) unless ArgumentMatchers::AnyArgsMatcher === args.first expected_args = if ArgumentMatchers::NoArgsMatcher === args.first - [] - elsif args.length > 0 - args - else - # No arguments given, this will raise. - super - end + [] + elsif args.length > 0 + args + else + # No arguments given, this will raise. + super + end validate_expected_arguments!(expected_args) end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index b9349cf9b..44ed76d6c 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -3,7 +3,6 @@ module RSpec module Mocks - # @private module VerifyingProxyMethods def add_stub(method_name, opts={}, &implementation) @@ -22,21 +21,20 @@ def add_message_expectation(method_name, opts={}, &block) end def ensure_implemented(method_name) - if method_reference[method_name].unimplemented? - @error_generator.raise_unimplemented_error( - @doubled_module, - method_name - ) - end + return unless method_reference[method_name].unimplemented? + + @error_generator.raise_unimplemented_error( + @doubled_module, + method_name + ) end - def ensure_publicly_implemented(method_name, object) + def ensure_publicly_implemented(method_name, _object) ensure_implemented(method_name) visibility = method_reference[method_name].visibility - unless visibility == :public - @error_generator.raise_non_public_error(method_name, visibility) - end + return if visibility == :public + @error_generator.raise_non_public_error(method_name, visibility) end end @@ -133,9 +131,7 @@ def proxy_method_invoked(obj, *args, &block) def validate_arguments!(actual_args) @method_reference.with_signature do |signature| verifier = Support::StrictSignatureVerifier.new(signature, actual_args) - unless verifier.valid? - raise ArgumentError, verifier.error_message - end + raise ArgumentError, verifier.error_message unless verifier.valid? end end end From 364d7a7004997e5f8fda2ce7de3df37b3ea7f394 Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Mon, 1 Sep 2014 07:26:15 +0100 Subject: [PATCH 0704/1179] Fix prepended module detection on Ruby 2.0 Ruby's behaviour when prepending a module to a singleton class differs between 2.0 and 2.1. I wrote a test case to illustrate the differences: https://gist.github.com/eugeneius/884873965f90092fc50c On Ruby 2.1, the singleton class always appears in its own ancestors chain. Prepended modules come before it, and included modules after it. This is consistent with how the ancestors chain works for normal classes and modules. On Ruby 2.0, the singleton class only appears in its own ancestors chain if a module has been prepended to it. This means we can't find the prepended modules by just iterating over its ancestors until some stopping condition. Right now included modules are being incorrectly classified as prepended modules on Ruby 2.0 - I've included a spec here capturing the bug that fails on master. If the singleton class is not in its own ancestors chain, we know there aren't any prepended modules, so we don't need to check for them. The existing code works in all other cases. --- lib/rspec/mocks/proxy.rb | 8 ++++++-- spec/rspec/mocks/stub_spec.rb | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 7b7110028..e82cac7e4 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -208,8 +208,12 @@ def visibility_for(_method_name) def prepended_modules_of_singleton_class @prepended_modules_of_singleton_class ||= begin singleton_class = @object.singleton_class - singleton_class.ancestors.take_while do |mod| - !(Class === mod || @object.equal?(mod)) + if singleton_class.ancestors.include?(singleton_class) + singleton_class.ancestors.take_while do |mod| + !(Class === mod || @object.equal?(mod)) + end + else + [] end end end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index ee9fd0278..2fbd65690 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -155,6 +155,16 @@ def foo; :bar; end }.not_to change { mod.singleton_class.ancestors } end + it 'does not unnecessarily prepend a module when the module was included' do + object = Object.new + def object.value; :original; end + object.singleton_class.send(:include, ToBePrepended) + + expect { + allow(object).to receive(:value) { :stubbed } + }.not_to change { object.singleton_class.ancestors } + end + it 'reuses our prepend module so as not to keep mutating the ancestors' do object = Object.new def object.value; :original; end From c78362706296ecfa3cf9f857fbf74c48347909d3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 1 Sep 2014 19:31:07 +0100 Subject: [PATCH 0705/1179] changelog for #771 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index a163e975d..c20d4ac31 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,6 +21,8 @@ Bugfixes: * Remove `any_number_of_times` from `any_instance` recorders that were erroneously causing mention of the method in documentation. (Jon Rowe, #760) +* Prevent included modules being detected as prepended modules on Ruby 2.0. + (Eugene Kenny, #771) ### 3.0.4 / 2014-08-14 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3.0.4) From 689c1330d5363a60fe33e793f4a443ca412c4888 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 1 Aug 2014 16:27:19 +1000 Subject: [PATCH 0706/1179] spec out #745 (This has since been solved by #771). --- spec/rspec/mocks/stub_spec.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 2fbd65690..760c97045 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -300,6 +300,30 @@ class << self; public :hello; end; expect(mod.hello).to eq(:hello) end + it "correctly handles stubbing inherited mixed in class methods" do + mod = Module.new do + def method_a + raise "should not execute method_a" + end + + def self.included(other) + other.extend self + end + end + + a = Class.new { include mod } + b = Class.new(a) do + def self.method_b + "executed method_b" + end + end + + allow(a).to receive(:method_a) + allow(b).to receive(:method_b).and_return("stubbed method_b") + + expect(b.method_b).to eql("stubbed method_b") + end + if Support::RubyFeatures.module_prepends_supported? context "with a prepended module (ruby 2.0.0+)" do module ToBePrepended From 536b66be0bf228f3bd08c756717a49402d68e083 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 1 Sep 2014 22:15:20 -0700 Subject: [PATCH 0707/1179] Refactor `prepended_modules_of_singleton_class`. The `take_while` implementation was a bit obtuse. It is simpler to just find the index of the singleton class and then just return all ancestors before it. This ensures that only modules which are prepended onto the specific singleton class we are dealing with (and not the singleton class of a superclass...) are considered, which was the root of the problem causing #745. --- lib/rspec/mocks/proxy.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index e82cac7e4..9d67ea03a 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -208,13 +208,13 @@ def visibility_for(_method_name) def prepended_modules_of_singleton_class @prepended_modules_of_singleton_class ||= begin singleton_class = @object.singleton_class - if singleton_class.ancestors.include?(singleton_class) - singleton_class.ancestors.take_while do |mod| - !(Class === mod || @object.equal?(mod)) - end - else - [] - end + ancestors = singleton_class.ancestors + + # `|| 0` is necessary for Ruby 2.0, where the singleton class + # is only in the ancestor list when there are prepended modules. + singleton_index = ancestors.index(singleton_class) || 0 + + ancestors[0, singleton_index] end end end From 06fbd3dc2800f9674b7976c0c993162e67a70b12 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 4 Sep 2014 18:04:26 -0700 Subject: [PATCH 0708/1179] 3.1.0 release. --- Changelog.md | 4 ++-- lib/rspec/mocks/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index c20d4ac31..9795c7c59 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.1.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3.0.4) +### 3.1.0 / 2014-09-04 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.4...v3.1.0) Enhancements: diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 6c8d74638..dbb32256d 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.1.0.pre' + STRING = '3.1.0' end end end From 9e261ad968be31b0c980896ed7261f4e13ceeff9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 4 Sep 2014 19:28:32 -0700 Subject: [PATCH 0709/1179] Bump version to 3.2.0.pre. --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index dbb32256d..f18e4652e 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.1.0' + STRING = '3.2.0.pre' end end end From 3db6ca7feca8a5a4e167abaff631fd02756a3560 Mon Sep 17 00:00:00 2001 From: Tony Novak Date: Mon, 15 Sep 2014 15:03:43 -0700 Subject: [PATCH 0710/1179] fixes #781 --- lib/rspec/mocks/any_instance/recorder.rb | 2 +- lib/rspec/mocks/proxy.rb | 19 ++++++++++--------- spec/rspec/mocks/any_instance_spec.rb | 10 ++++++++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 19c210a6c..271348b4a 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -248,7 +248,7 @@ def mark_invoked!(method_name) if Support::RubyFeatures.module_prepends_supported? def allow_no_prepended_module_definition_of(method_name) - prepended_modules = @klass.ancestors.take_while { |mod| !(Class === mod) } + prepended_modules = RSpec::Mocks::Proxy.prepended_modules_of(@klass) problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) } return unless problem_mod diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 9d67ea03a..ce7a6dbc8 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -205,17 +205,18 @@ def visibility_for(_method_name) end if Support::RubyFeatures.module_prepends_supported? - def prepended_modules_of_singleton_class - @prepended_modules_of_singleton_class ||= begin - singleton_class = @object.singleton_class - ancestors = singleton_class.ancestors + def self.prepended_modules_of(klass) + ancestors = klass.ancestors - # `|| 0` is necessary for Ruby 2.0, where the singleton class - # is only in the ancestor list when there are prepended modules. - singleton_index = ancestors.index(singleton_class) || 0 + # `|| 0` is necessary for Ruby 2.0, where the singleton class + # is only in the ancestor list when there are prepended modules. + singleton_index = ancestors.index(klass) || 0 - ancestors[0, singleton_index] - end + ancestors[0, singleton_index] + end + + def prepended_modules_of_singleton_class + @prepended_modules_of_singleton_class ||= RSpec::Mocks::Proxy.prepended_modules_of(@object.singleton_class) end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index cdc51b189..e427aace1 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -523,6 +523,16 @@ def inspect end end + context "when the class has an included module" do + it 'allows mocking a method that is defined on the module' do + mod = Module.new { def foo; end } + klass.class_eval { include mod } + expect_any_instance_of(mod).to receive(:foo).and_return(45) + + expect(klass.new.foo).to eq(45) + end + end + context "when an instance has been directly stubbed" do it "fails when a second instance to receive the message" do expect_any_instance_of(klass).to receive(:foo) From 94dda94921a0c578f3a3cd1f4a615167203b1194 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 16 Sep 2014 08:20:26 +1000 Subject: [PATCH 0711/1179] changelog for #781 [skip ci] --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index 9795c7c59..23eb591b5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### 3.1.1 / Developmemt +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...master) + +Bugfixes: + +* Prevent included modules being detected as prepended modules on Ruby 2.0 + when using `any_instance_of(...)` (Tony Novak, #781) + ### 3.1.0 / 2014-09-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.4...v3.1.0) From c4c85cc60804ba351a4b183b1ea0e3851de9a345 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 17 Sep 2014 23:15:36 -0700 Subject: [PATCH 0712/1179] Fix regexp warnings on 1.8.7. ./spec/rspec/mocks/argument_matchers_spec.rb:277: warning: regexp has invalid interval ./spec/rspec/mocks/argument_matchers_spec.rb:277: warning: regexp has `}' without escape ./spec/rspec/mocks/argument_matchers_spec.rb:284: warning: regexp has invalid interval ./spec/rspec/mocks/argument_matchers_spec.rb:284: warning: regexp has `}' without escape --- spec/rspec/mocks/argument_matchers_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index b3ef95abc..c8b766ca1 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -274,14 +274,14 @@ module Mocks expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") expect do a_double.random_call(:a => "b", :c => "e") - end.to fail_matching(/expected: \({(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")}\)/) + end.to fail_matching(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/) end it "fails for a hash w/ wrong keys", :reset => true do expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") expect do a_double.random_call("a" => "b", "c" => "d") - end.to fail_matching(/expected: \({(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")}\)/) + end.to fail_matching(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/) end it "matches a class against itself" do From 26f27b3edc7fb2e5e4d8f35592aedb50b4dcd7df Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 17 Sep 2014 23:41:42 -0700 Subject: [PATCH 0713/1179] Skip spec warnings spec on 1.9.2. We load psych and syck and they trigger warnings. --- spec/rspec/mocks_spec.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 97fd6618e..5f1f540f9 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -6,7 +6,14 @@ # Must be required before other files due to how our autoloads are setup. # (Users won't hit this problem because they won't require all the files # individually in whatever order the file system returns) - 'require "rspec/mocks/any_instance"' + 'require "rspec/mocks/any_instance"' do + + if RUBY_VERSION == '1.9.2' + before(:example, :description => /spec files/) do + pending "Loading psych and syck on 1.9.2 (as our test suite does) triggers warnings" + end + end + end describe ".verify" do it "delegates to the space" do From 7db36a61205907f8b09d89d9c2b45ac47c0b2fa8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Sep 2014 09:49:02 -0700 Subject: [PATCH 0714/1179] Forwardport 3.1.1 release notes. [ci skip] --- Changelog.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 23eb591b5..a14ed11e6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,13 @@ -### 3.1.1 / Developmemt -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...master) +### 3.2.0 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...master) -Bugfixes: +### 3.1.1 / 2014-09-18 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) + +Bug Fixes: * Prevent included modules being detected as prepended modules on Ruby 2.0 - when using `any_instance_of(...)` (Tony Novak, #781) + when using `any_instance_of(...)`. (Tony Novak, #781) ### 3.1.0 / 2014-09-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.4...v3.1.0) @@ -25,7 +28,7 @@ Enhancements: * Add `and_wrap_original` modifier for partial doubles to mutate the response from a method. (Jon Rowe, #762) -Bugfixes: +Bug Fixes: * Remove `any_number_of_times` from `any_instance` recorders that were erroneously causing mention of the method in documentation. (Jon Rowe, #760) From 01cf92efdedfdf12ffd8e6f4b46913e0d0e37bdd Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 21 Sep 2014 12:07:34 +1000 Subject: [PATCH 0715/1179] raise an error when attempting to use allow(...).to have_received --- lib/rspec/mocks/matchers/have_received.rb | 6 ++++++ spec/rspec/mocks/matchers/have_received_spec.rb | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 3fcfe7090..66dcc1646 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -54,6 +54,12 @@ def description end end + def setup_allowance(_subject, &_block) + raise RSpec::Mocks::MockExpectationError, + "Using allow(...) with the `have_received` matcher is not "\ + "supported as it would have no effect." + end + private def expect diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index c56e3b55b..141aab548 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -459,6 +459,14 @@ module Mocks end end + describe "allow(...).to have_received" do + it "fails because its nonsensical" do + expect { + allow(double).to have_received(:some_method) + }.to fail_with("Using allow(...) with the `have_received` matcher is not supported as it would have no effect.") + end + end + def double_with_met_expectation(method_name, *args) double = double_with_unmet_expectation(method_name) meet_expectation(double, method_name, *args) From 5b3d6ff3033181fd589368210dbcc58a98e9c128 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 22 Sep 2014 15:06:39 +1000 Subject: [PATCH 0716/1179] tweak ws [skip ci] --- lib/rspec/mocks/matchers/have_received.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 66dcc1646..2a1ee261a 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -56,7 +56,7 @@ def description def setup_allowance(_subject, &_block) raise RSpec::Mocks::MockExpectationError, - "Using allow(...) with the `have_received` matcher is not "\ + "Using allow(...) with the `have_received` matcher is not " \ "supported as it would have no effect." end From 085d79aed3f249879ccfa320410b435758065161 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 22 Sep 2014 15:09:10 +1000 Subject: [PATCH 0717/1179] changelog for #788 [skip ci] --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index a14ed11e6..3a3e12639 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### 3.2.0 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...master) +Enhancements: + +* Improve error message caused when you use `allow(...)` with the + `have_received` matcher. (Jon Rowe, #788). + ### 3.1.1 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) From d3e6a4effc864b1edd673397319cf7bb992258bd Mon Sep 17 00:00:00 2001 From: dB Date: Tue, 23 Sep 2014 11:30:48 -0400 Subject: [PATCH 0718/1179] Explicitly call out args passed into any_instance stubs. --- features/old_syntax/any_instance.feature | 6 +++--- features/working_with_legacy_code/any_instance.feature | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/features/old_syntax/any_instance.feature b/features/old_syntax/any_instance.feature index d561303a9..0a4d85ff7 100644 --- a/features/old_syntax/any_instance.feature +++ b/features/old_syntax/any_instance.feature @@ -71,11 +71,11 @@ Feature: `any_instance` """ruby RSpec.describe "Stubbing any instance of a class" do it 'yields the receiver to the block implementation' do - Array.any_instance.stub(:shuffle) do |array| - array.unshift(array.pop) + String.any_instance.stub(:slice) do |value, start, length| + value[start, length] end - expect([1, 2, 3].shuffle).to eq([3, 1, 2]) + expect('string'.slice(2, 3)).to eq('rin') end end """ diff --git a/features/working_with_legacy_code/any_instance.feature b/features/working_with_legacy_code/any_instance.feature index f2bb5ca1f..8650bfce9 100644 --- a/features/working_with_legacy_code/any_instance.feature +++ b/features/working_with_legacy_code/any_instance.feature @@ -12,7 +12,7 @@ Feature: Any Instance These methods add the appropriate stub or expectation to all instances of `Widget`. This feature is sometimes useful when working with legacy code, though in general we - discourage its use for a number of reasons: + discourage its use for a number of reasons: * The `rspec-mocks` API is designed for individual object instances, but this feature operates on entire classes of objects. As a result there are some sematically confusing @@ -81,11 +81,11 @@ Feature: Any Instance """ruby RSpec.describe "allow_any_instance_of" do it 'yields the receiver to the block implementation' do - allow_any_instance_of(Array).to receive(:shuffle) do |array| - array.unshift(array.pop) + allow_any_instance_of(String).to receive(:slice) do |value, start, length| + value[start, length] end - expect([1, 2, 3].shuffle).to eq([3, 1, 2]) + expect('string'.slice(2, 3)).to eq('rin') end end """ From f873361f4ad685974158bf0475ac75534b0ac11d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 26 Sep 2014 16:56:14 +1000 Subject: [PATCH 0719/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 ++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 5a03e132a..8de3db276 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 371495bd6..67cc66393 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -18,6 +18,7 @@ rvm: - 2.1.0 - 2.1.1 - 2.1.2 + - 2.1.3 - ruby-head - ree - jruby-18mode diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f57555608..d03e9f6f3 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 5c7e3d0da..daf776e19 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index 10c1c2309..eb356b8bf 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From 06a490ef8ca02b481832f7d032248490d6f6eee3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 26 Sep 2014 09:39:03 -0700 Subject: [PATCH 0720/1179] Forwardport 3.1.2 release notes. --- Changelog.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3a3e12639..760f3bfac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,14 @@ ### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...master) -Enhancements: +### 3.1.2 / 2014-09-26 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...v3.1.2) + +Bug Fixes: -* Improve error message caused when you use `allow(...)` with the - `have_received` matcher. (Jon Rowe, #788). +* Provide a clear error message when you use `allow(...)` with the + `have_received` matcher (they are not intended to be used together + and previously caused an odd internal failure in rspec-mocks). (Jon Rowe, #788). ### 3.1.1 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) From 20a3ae3d588e7c620ef46294617cbe36a9291ed7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 27 Sep 2014 16:49:37 -0700 Subject: [PATCH 0721/1179] Reorganize verified double specs. The single `verifying_double_spec.rb` has gotten unwieldy and hard to work with because it's so large. It's easier to work with smaller spec files. - Extract doubled class definitions into a support file. - Move the method visibility specs into their own file. - Move class/object/instance double specs into their own files. - Move general verifying double construction specs into a separate file. --- spec/rspec/mocks/verifying_double_spec.rb | 690 ------------------ .../class_double_with_class_loaded_spec.rb | 111 +++ ...class_double_with_class_not_loaded_spec.rb | 58 ++ .../verifying_doubles/construction_spec.rb | 54 ++ .../instance_double_with_class_loaded_spec.rb | 188 +++++ ...tance_double_with_class_not_loaded_spec.rb | 57 ++ .../method_visibility_spec.rb | 129 ++++ .../verifying_doubles/object_double_spec.rb | 61 ++ spec/support/doubled_classes.rb | 68 ++ 9 files changed, 726 insertions(+), 690 deletions(-) delete mode 100644 spec/rspec/mocks/verifying_double_spec.rb create mode 100644 spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb create mode 100644 spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb create mode 100644 spec/rspec/mocks/verifying_doubles/construction_spec.rb create mode 100644 spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb create mode 100644 spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb create mode 100644 spec/rspec/mocks/verifying_doubles/method_visibility_spec.rb create mode 100644 spec/rspec/mocks/verifying_doubles/object_double_spec.rb create mode 100644 spec/support/doubled_classes.rb diff --git a/spec/rspec/mocks/verifying_double_spec.rb b/spec/rspec/mocks/verifying_double_spec.rb deleted file mode 100644 index 937da7cfa..000000000 --- a/spec/rspec/mocks/verifying_double_spec.rb +++ /dev/null @@ -1,690 +0,0 @@ -class LoadedClass - extend RSpec::Support::RubyFeatures - - M = :m - N = :n - INSTANCE = LoadedClass.new - - class << self - - def respond_to?(method_name, include_all = false) - return true if method_name == :dynamic_class_method - super - end - - def defined_class_method - end - - def send - # fake out! - end - - protected - - def defined_protected_class_method - end - - private - - def defined_private_class_method - end - - end - - def defined_instance_method - end - - if required_kw_args_supported? - # Need to eval this since it is invalid syntax on earlier rubies. - eval <<-RUBY - def kw_args_method(foo, optional_arg:'hello', required_arg:) - end - - def mixed_args_method(foo, bar, optional_arg_1:1, optional_arg_2:2) - end - RUBY - end - - def send(*) - end - - def respond_to?(method_name, include_all = false) - return true if method_name == :dynamic_instance_method - super - end - - class Nested; end - -protected - - def defined_protected_method - end - -private - - def defined_private_method - "wink wink ;)" - end - -end - -module RSpec - module Mocks - RSpec.describe 'verifying doubles' do - describe 'instance doubles' do - describe 'when doubled class is not loaded' do - include_context "with isolated configuration" - - before do - RSpec::Mocks.configuration.verify_doubled_constant_names = false - end - - it 'includes the double name in errors for unexpected messages' do - o = instance_double("NonLoadedClass") - expect { - o.foo - }.to fail_matching('Double "NonLoadedClass (instance)"') - end - - it 'allows any instance method to be stubbed' do - o = instance_double('NonloadedClass') - allow(o).to receive(:undefined_instance_method).with(:arg).and_return(true) - expect(o.undefined_instance_method(:arg)).to eq(true) - end - - it 'allows any instance method to be expected' do - o = instance_double("NonloadedClass") - - expect(o).to receive(:undefined_instance_method). - with(:arg). - and_return(true) - - expect(o.undefined_instance_method(:arg)).to eq(true) - end - - it 'handles classes that are materialized after mocking' do - stub_const "A::B", Object.new - o = instance_double "A", :undefined_instance_method => true - - expect(o.undefined_instance_method).to eq(true) - end - - context 'for null objects' do - let(:o) { instance_double('NonLoadedClass').as_null_object } - - it 'returns self from any message' do - expect(o.a.b.c).to be(o) - end - - it 'reports it responds to any message' do - expect(o.respond_to?(:a)).to be true - expect(o.respond_to?(:a, false)).to be true - expect(o.respond_to?(:a, true)).to be true - end - end - end - - describe 'when doubled class is loaded' do - include_context "with isolated configuration" - - before do - RSpec::Mocks.configuration.verify_doubled_constant_names = true - end - - it 'includes the double name in errors for unexpected messages' do - o = instance_double("LoadedClass") - expect { - o.defined_instance_method - }.to fail_matching('Double "LoadedClass (instance)"') - end - - it 'only allows instance methods that exist to be stubbed' do - o = instance_double('LoadedClass', :defined_instance_method => 1) - expect(o.defined_instance_method).to eq(1) - - prevents { allow(o).to receive(:undefined_instance_method) } - prevents { allow(o).to receive(:defined_class_method) } - end - - it 'only allows instance methods that exist to be expected' do - o = instance_double('LoadedClass') - expect(o).to receive(:defined_instance_method) - o.defined_instance_method - - prevents { expect(o).to receive(:undefined_instance_method) } - prevents { expect(o).to receive(:defined_class_method) } - prevents { expect(o).to receive(:undefined_instance_method) } - prevents { expect(o).to receive(:defined_class_method) } - end - - it 'allows `send` to be stubbed if it is defined on the class' do - o = instance_double('LoadedClass') - allow(o).to receive(:send).and_return("received") - expect(o.send(:msg)).to eq("received") - end - - it 'gives a descriptive error message for NoMethodError' do - o = instance_double("LoadedClass") - expect { - o.defined_private_method - }.to raise_error(NoMethodError, - /Double "LoadedClass \(instance\)"/) - end - - describe "method visibility" do - shared_examples "preserves method visibility" do |visibility| - method_name = :"defined_#{visibility}_method" - - it "can allow a #{visibility} instance method" do - o = instance_double('LoadedClass') - allow(o).to receive(method_name).and_return(3) - expect(o.send method_name).to eq(3) - end - - it "can expect a #{visibility} instance method" do - o = instance_double('LoadedClass') - expect(o).to receive(method_name) - o.send method_name - end - - it "preserves #{visibility} visibility when allowing a #{visibility} method" do - preserves_visibility(method_name, visibility) do - instance_double('LoadedClass').tap do |o| - allow(o).to receive(method_name) - end - end - end - - it "preserves #{visibility} visibility when expecting a #{visibility} method" do - preserves_visibility(method_name, visibility) do - instance_double('LoadedClass').tap do |o| - expect(o).to receive(method_name).at_least(:once) - o.send(method_name) # to satisfy the expectation - end - end - end - - it "preserves #{visibility} visibility on a null object" do - preserves_visibility(method_name, visibility) do - instance_double('LoadedClass').as_null_object - end - end - end - - include_examples "preserves method visibility", :private - include_examples "preserves method visibility", :protected - end - - it 'does not allow dynamic methods to be expected' do - # This isn't possible at the moment since an instance of the class - # would be required for the verification, and we only have the - # class itself. - # - # This spec exists as "negative" documentation of the absence of a - # feature, to highlight the asymmetry from class doubles (that do - # support this behaviour). - prevents { - instance_double('LoadedClass', :dynamic_instance_method => 1) - } - end - - it 'checks the arity of stubbed methods' do - o = instance_double('LoadedClass') - prevents { - expect(o).to receive(:defined_instance_method).with(:a) - } - end - - it 'checks that stubbed methods are invoked with the correct arity' do - o = instance_double('LoadedClass', :defined_instance_method => 25) - expect { - o.defined_instance_method(:a) - }.to raise_error(ArgumentError, - "Wrong number of arguments. Expected 0, got 1.") - end - - if required_kw_args_supported? - it 'allows keyword arguments' do - o = instance_double('LoadedClass', :kw_args_method => true) - expect(o.kw_args_method(1, :required_arg => 'something')).to eq(true) - end - - context 'for a method that only accepts keyword args' do - it 'allows hash matchers like `hash_including` to be used in place of the keywords arg hash' do - o = instance_double('LoadedClass') - expect(o).to receive(:kw_args_method). - with(1, hash_including(:required_arg => 1)) - o.kw_args_method(1, :required_arg => 1) - end - - it 'allows anything matcher to be used in place of the keywords arg hash' do - o = instance_double('LoadedClass') - expect(o).to receive(:kw_args_method).with(1, anything) - o.kw_args_method(1, :required_arg => 1) - end - - it 'still checks positional arguments when matchers used for keyword args' do - o = instance_double('LoadedClass') - prevents(/Expected 1, got 3/) { - expect(o).to receive(:kw_args_method). - with(1, 2, 3, hash_including(:required_arg => 1)) - } - end - - it 'does not allow matchers to be used in an actual method call' do - o = instance_double('LoadedClass') - matcher = hash_including(:required_arg => 1) - allow(o).to receive(:kw_args_method).with(1, matcher) - expect { - o.kw_args_method(1, matcher) - }.to raise_error(ArgumentError) - end - end - - context 'for a method that accepts a mix of optional keyword and positional args' do - it 'allows hash matchers like `hash_including` to be used in place of the keywords arg hash' do - o = instance_double('LoadedClass') - expect(o).to receive(:mixed_args_method).with(1, 2, hash_including(:optional_arg_1 => 1)) - o.mixed_args_method(1, 2, :optional_arg_1 => 1) - end - end - - it 'checks that stubbed methods with required keyword args are ' + - 'invoked with the required arguments' do - o = instance_double('LoadedClass', :kw_args_method => true) - expect { - o.kw_args_method(:optional_arg => 'something') - }.to raise_error(ArgumentError) - end - end - - it 'allows class to be specified by constant' do - o = instance_double(LoadedClass, :defined_instance_method => 1) - expect(o.defined_instance_method).to eq(1) - end - - context 'for null objects' do - let(:o) { instance_double('LoadedClass').as_null_object } - - it 'only allows defined methods' do - expect(o.defined_instance_method).to eq(o) - prevents { o.undefined_method } - prevents { o.send(:undefined_method) } - prevents { o.__send__(:undefined_method) } - end - - it "includes the double's name in a private method error" do - expect { - o.rand - }.to raise_error(NoMethodError, %r{private.*Double "LoadedClass \(instance\)"}) - end - - it 'reports what public messages it responds to accurately' do - expect(o.respond_to?(:defined_instance_method)).to be true - expect(o.respond_to?(:defined_instance_method, true)).to be true - expect(o.respond_to?(:defined_instance_method, false)).to be true - - expect(o.respond_to?(:undefined_method)).to be false - expect(o.respond_to?(:undefined_method, true)).to be false - expect(o.respond_to?(:undefined_method, false)).to be false - end - - it 'reports that it responds to defined private methods when the appropriate arg is passed' do - expect(o.respond_to?(:defined_private_method)).to be false - expect(o.respond_to?(:defined_private_method, true)).to be true - expect(o.respond_to?(:defined_private_method, false)).to be false - end - - if RUBY_VERSION.to_f < 2.0 # respond_to?(:protected_method) changed behavior in Ruby 2.0. - it 'reports that it responds to protected methods' do - expect(o.respond_to?(:defined_protected_method)).to be true - expect(o.respond_to?(:defined_protected_method, true)).to be true - expect(o.respond_to?(:defined_protected_method, false)).to be true - end - else - it 'reports that it responds to protected methods when the appropriate arg is passed' do - expect(o.respond_to?(:defined_protected_method)).to be false - expect(o.respond_to?(:defined_protected_method, true)).to be true - expect(o.respond_to?(:defined_protected_method, false)).to be false - end - end - end - end - - it 'cannot be constructed with a non-module object' do - expect { - instance_double(Object.new) - }.to raise_error(/Module or String expected/) - end - - it 'can be constructed with a struct' do - o = instance_double(Struct.new(:defined_method), :defined_method => 1) - - expect(o.defined_method).to eq(1) - end - end - - describe 'class doubles' do - describe 'when doubled class is not loaded' do - include_context "with isolated configuration" - - before do - RSpec::Mocks.configuration.verify_doubled_constant_names = false - end - - it 'includes the double name in errors for unexpected messages' do - o = class_double("NonLoadedClass") - expect { - o.foo - }.to fail_matching('Double "NonLoadedClass"') - end - - it 'allows any method to be stubbed' do - o = class_double('NonloadedClass') - allow(o).to receive(:undefined_instance_method).with(:arg).and_return(1) - expect(o.undefined_instance_method(:arg)).to eq(1) - end - - context "when stubbing a private module method" do - before(:all) do - Module.class_exec do - private - def use; end - end - end - - after(:all) do - Module.class_exec do - undef use - end - end - - it 'can mock private module methods' do - double = Module.new - allow(double).to receive(:use) - expect { double.use }.to raise_error(/private method `use' called/) - - double = class_double("NonloadedClass") - expect(double).to receive(:use).and_return(:ok) - expect(double.use).to be(:ok) - end - end - end - - describe 'when doubled class is loaded' do - include_context "with isolated configuration" - - before do - RSpec::Mocks.configuration.verify_doubled_constant_names = true - end - - it 'includes the double name in errors for unexpected messages' do - o = class_double("LoadedClass") - expect { - o.defined_class_method - }.to fail_matching('Double "LoadedClass"') - end - - it 'only allows class methods that exist to be stubbed' do - o = class_double('LoadedClass', :defined_class_method => 1) - expect(o.defined_class_method).to eq(1) - - prevents { allow(o).to receive(:undefined_instance_method) } - prevents { allow(o).to receive(:defined_instance_method) } - end - - it 'only allows class methods that exist to be expected' do - o = class_double('LoadedClass') - expect(o).to receive(:defined_class_method) - o.defined_class_method - - prevents { expect(o).to receive(:undefined_instance_method) } - prevents { expect(o).to receive(:defined_instance_method) } - prevents { expect(o).to receive(:undefined_instance_method) } - prevents { expect(o).to receive(:defined_instance_method) } - end - - it 'gives a descriptive error message for NoMethodError' do - o = class_double("LoadedClass") - expect { - o.defined_private_class_method - }.to raise_error(NoMethodError, /Double "LoadedClass"/) - end - - describe "method visibility" do - shared_examples "preserves method visibility" do |visibility| - method_name = :"defined_#{visibility}_class_method" - - it "can allow a #{visibility} instance method" do - o = class_double('LoadedClass') - allow(o).to receive(method_name).and_return(3) - expect(o.send method_name).to eq(3) - end - - it "can expect a #{visibility} instance method" do - o = class_double('LoadedClass') - expect(o).to receive(method_name) - o.send method_name - end - - it "preserves #{visibility} visibility when allowing a #{visibility} method" do - preserves_visibility(method_name, visibility) do - class_double('LoadedClass').tap do |o| - allow(o).to receive(method_name) - end - end - end - - it "preserves #{visibility} visibility when expecting a #{visibility} method" do - preserves_visibility(method_name, visibility) do - class_double('LoadedClass').tap do |o| - expect(o).to receive(method_name).at_least(:once) - o.send(method_name) # to satisfy the expectation - end - end - end - - it "preserves #{visibility} visibility on a null object" do - preserves_visibility(method_name, visibility) do - class_double('LoadedClass').as_null_object - end - end - end - - include_examples "preserves method visibility", :private - include_examples "preserves method visibility", :protected - end - - it 'checks that stubbed methods are invoked with the correct arity' do - o = class_double('LoadedClass', :defined_class_method => 1) - expect { - o.defined_class_method(:a) - }.to raise_error(ArgumentError) - end - - it 'allows dynamically defined class method stubs with arguments' do - o = class_double('LoadedClass') - allow(o).to receive(:dynamic_class_method).with(:a) { 1 } - - expect(o.dynamic_class_method(:a)).to eq(1) - end - - it 'allows dynamically defined class method mocks with arguments' do - o = class_double('LoadedClass') - expect(o).to receive(:dynamic_class_method).with(:a) - - o.dynamic_class_method(:a) - end - - it 'allows dynamically defined class methods to be expected' do - o = class_double('LoadedClass', :dynamic_class_method => 1) - expect(o.dynamic_class_method).to eq(1) - end - - it 'allows class to be specified by constant' do - o = class_double(LoadedClass, :defined_class_method => 1) - expect(o.defined_class_method).to eq(1) - end - - it 'can replace existing constants for the duration of the test' do - original = LoadedClass - object = class_double('LoadedClass').as_stubbed_const - expect(object).to receive(:defined_class_method) - - expect(LoadedClass).to eq(object) - ::RSpec::Mocks.teardown - ::RSpec::Mocks.setup - expect(LoadedClass).to eq(original) - end - - it 'can transfer nested constants to the double' do - class_double("LoadedClass"). - as_stubbed_const(:transfer_nested_constants => true) - expect(LoadedClass::M).to eq(:m) - expect(LoadedClass::N).to eq(:n) - end - - it 'correctly verifies expectations when constant is removed' do - dbl1 = class_double(LoadedClass::Nested).as_stubbed_const - class_double(LoadedClass).as_stubbed_const - - prevents { - expect(dbl1).to receive(:undefined_class_method) - } - end - - it 'only allows defined methods for null objects' do - o = class_double('LoadedClass').as_null_object - - expect(o.defined_class_method).to eq(o) - prevents { o.undefined_method } - end - end - - it 'cannot be constructed with a non-module object' do - expect { - class_double(Object.new) - }.to raise_error(/Module or String expected/) - end - - it "trying to raise a class_double raises a TypeError", :unless => RUBY_VERSION == '1.9.2' do - subject = Object.new - class_double("StubbedError").as_stubbed_const - allow(subject).to receive(:some_method).and_raise(StubbedError) - expect { subject.some_method }.to raise_error(TypeError, 'exception class/object expected') - end - - end - - describe 'object doubles' do - it 'replaces an unloaded constant' do - o = object_double("LoadedClass::NOINSTANCE").as_stubbed_const - - expect(LoadedClass::NOINSTANCE).to eq(o) - - expect(o).to receive(:undefined_instance_method) - o.undefined_instance_method - end - - it 'replaces a constant by name and verifies instances methods' do - o = object_double("LoadedClass::INSTANCE").as_stubbed_const - - expect(LoadedClass::INSTANCE).to eq(o) - - prevents { expect(o).to receive(:undefined_instance_method) } - prevents { expect(o).to receive(:defined_class_method) } - prevents { o.defined_instance_method } - - expect(o).to receive(:defined_instance_method) - o.defined_instance_method - expect(o).to receive(:defined_private_method) - o.send :defined_private_method - end - - it 'can create a double that matches the interface of any arbitrary object' do - o = object_double(LoadedClass.new) - - prevents { expect(o).to receive(:undefined_instance_method) } - prevents { expect(o).to receive(:defined_class_method) } - prevents { o.defined_instance_method } - - expect(o).to receive(:defined_instance_method) - o.defined_instance_method - expect(o).to receive(:defined_private_method) - o.send :defined_private_method - end - - it 'does not allow transferring constants to an object' do - expect { - object_double("LoadedClass::INSTANCE"). - as_stubbed_const(:transfer_nested_constants => true) - }.to raise_error(/Cannot transfer nested constants/) - end - - it 'does not allow as_stubbed_constant for real objects' do - expect { - object_double(LoadedClass.new).as_stubbed_const - }.to raise_error(/Can not perform constant replacement with an object/) - end - - it 'is not a module' do - expect(object_double("LoadedClass::INSTANCE")).to_not be_a(Module) - end - end - - - describe 'when verify_doubled_constant_names config option is set' do - include_context "with isolated configuration" - - before do - RSpec::Mocks.configuration.verify_doubled_constant_names = true - end - - it 'prevents creation of instance doubles for unloaded constants' do - expect { - instance_double('LoadedClas') - }.to raise_error(VerifyingDoubleNotDefinedError) - end - - it 'prevents creation of class doubles for unloaded constants' do - expect { - class_double('LoadedClas') - }.to raise_error(VerifyingDoubleNotDefinedError) - end - end - - it 'can only be named with a string or a module' do - expect { instance_double(1) }.to raise_error(ArgumentError) - expect { instance_double(nil) }.to raise_error(ArgumentError) - end - - def preserves_visibility(method_name, visibility) - double = yield - - expect { - # send bypasses visbility, so we use eval instead. - eval("double.#{method_name}") - }.to raise_error(NoMethodError, a_message_indicating_visibility_violation(method_name, visibility)) - - expect { double.send(method_name) }.not_to raise_error - expect { double.__send__(method_name) }.not_to raise_error - - unless double.null_object? - # Null object doubles use `method_missing` and so the singleton class - # doesn't know what methods are defined. - singleton_class = class << double; self; end - expect(singleton_class.send("#{visibility}_method_defined?", method_name)).to be true - end - end - - RSpec::Matchers.define :a_message_indicating_visibility_violation do |method_name, visibility| - match do |msg| - # This should NOT Be just `msg.match(visibility)` because the method being called - # has the visibility name in it. We want to ensure it's a message that ruby is - # stating is of the given visibility. - msg.match("#{visibility} ") && msg.match(method_name.to_s) - end - end - end - end -end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb new file mode 100644 index 000000000..9c85c8b0d --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -0,0 +1,111 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec.describe 'A class double with the doubled class loaded' do + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = true + end + + it 'includes the double name in errors for unexpected messages' do + o = class_double("LoadedClass") + expect { + o.defined_class_method + }.to fail_matching('Double "LoadedClass"') + end + + it 'only allows class methods that exist to be stubbed' do + o = class_double('LoadedClass', :defined_class_method => 1) + expect(o.defined_class_method).to eq(1) + + prevents { allow(o).to receive(:undefined_instance_method) } + prevents { allow(o).to receive(:defined_instance_method) } + end + + it 'only allows class methods that exist to be expected' do + o = class_double('LoadedClass') + expect(o).to receive(:defined_class_method) + o.defined_class_method + + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_instance_method) } + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_instance_method) } + end + + it 'gives a descriptive error message for NoMethodError' do + o = class_double("LoadedClass") + expect { + o.defined_private_class_method + }.to raise_error(NoMethodError, /Double "LoadedClass"/) + end + + it 'checks that stubbed methods are invoked with the correct arity' do + o = class_double('LoadedClass', :defined_class_method => 1) + expect { + o.defined_class_method(:a) + }.to raise_error(ArgumentError) + end + + it 'allows dynamically defined class method stubs with arguments' do + o = class_double('LoadedClass') + allow(o).to receive(:dynamic_class_method).with(:a) { 1 } + + expect(o.dynamic_class_method(:a)).to eq(1) + end + + it 'allows dynamically defined class method mocks with arguments' do + o = class_double('LoadedClass') + expect(o).to receive(:dynamic_class_method).with(:a) + + o.dynamic_class_method(:a) + end + + it 'allows dynamically defined class methods to be expected' do + o = class_double('LoadedClass', :dynamic_class_method => 1) + expect(o.dynamic_class_method).to eq(1) + end + + it 'allows class to be specified by constant' do + o = class_double(LoadedClass, :defined_class_method => 1) + expect(o.defined_class_method).to eq(1) + end + + it 'can replace existing constants for the duration of the test' do + original = LoadedClass + object = class_double('LoadedClass').as_stubbed_const + expect(object).to receive(:defined_class_method) + + expect(LoadedClass).to eq(object) + ::RSpec::Mocks.teardown + ::RSpec::Mocks.setup + expect(LoadedClass).to eq(original) + end + + it 'can transfer nested constants to the double' do + class_double("LoadedClass"). + as_stubbed_const(:transfer_nested_constants => true) + expect(LoadedClass::M).to eq(:m) + expect(LoadedClass::N).to eq(:n) + end + + it 'correctly verifies expectations when constant is removed' do + dbl1 = class_double(LoadedClass::Nested).as_stubbed_const + class_double(LoadedClass).as_stubbed_const + + prevents { + expect(dbl1).to receive(:undefined_class_method) + } + end + + it 'only allows defined methods for null objects' do + o = class_double('LoadedClass').as_null_object + + expect(o.defined_class_method).to eq(o) + prevents { o.undefined_method } + end + end + end +end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb new file mode 100644 index 000000000..b89b696d5 --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb @@ -0,0 +1,58 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec.describe 'A class double with the doubled class not loaded' do + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = false + end + + it 'includes the double name in errors for unexpected messages' do + o = class_double("NonLoadedClass") + expect { + o.foo + }.to fail_matching('Double "NonLoadedClass"') + end + + it 'allows any method to be stubbed' do + o = class_double('NonloadedClass') + allow(o).to receive(:undefined_instance_method).with(:arg).and_return(1) + expect(o.undefined_instance_method(:arg)).to eq(1) + end + + specify "trying to raise a class_double raises a TypeError", :unless => RUBY_VERSION == '1.9.2' do + subject = Object.new + class_double("StubbedError").as_stubbed_const + allow(subject).to receive(:some_method).and_raise(StubbedError) + expect { subject.some_method }.to raise_error(TypeError, 'exception class/object expected') + end + + context "when stubbing a private module method" do + before(:all) do + Module.class_exec do + private + def use; end + end + end + + after(:all) do + Module.class_exec do + undef use + end + end + + it 'can mock private module methods' do + double = Module.new + allow(double).to receive(:use) + expect { double.use }.to raise_error(/private method `use' called/) + + double = class_double("NonloadedClass") + expect(double).to receive(:use).and_return(:ok) + expect(double.use).to be(:ok) + end + end + end + end +end diff --git a/spec/rspec/mocks/verifying_doubles/construction_spec.rb b/spec/rspec/mocks/verifying_doubles/construction_spec.rb new file mode 100644 index 000000000..cb5a9b8da --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/construction_spec.rb @@ -0,0 +1,54 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec.describe 'Constructing a verifying double' do + describe 'instance doubles' do + it 'cannot be constructed with a non-module object' do + expect { + instance_double(Object.new) + }.to raise_error(/Module or String expected/) + end + + it 'can be constructed with a struct' do + o = instance_double(Struct.new(:defined_method), :defined_method => 1) + + expect(o.defined_method).to eq(1) + end + end + + describe 'class doubles' do + it 'cannot be constructed with a non-module object' do + expect { + class_double(Object.new) + }.to raise_error(/Module or String expected/) + end + end + + describe 'when verify_doubled_constant_names config option is set' do + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = true + end + + it 'prevents creation of instance doubles for unloaded constants' do + expect { + instance_double('LoadedClas') + }.to raise_error(VerifyingDoubleNotDefinedError) + end + + it 'prevents creation of class doubles for unloaded constants' do + expect { + class_double('LoadedClas') + }.to raise_error(VerifyingDoubleNotDefinedError) + end + end + + it 'can only be named with a string or a module' do + expect { instance_double(1) }.to raise_error(ArgumentError) + expect { instance_double(nil) }.to raise_error(ArgumentError) + end + end + end +end diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb new file mode 100644 index 000000000..edb269bdd --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -0,0 +1,188 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec.describe 'An instance double with the doubled class loaded' do + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = true + end + + it 'includes the double name in errors for unexpected messages' do + o = instance_double("LoadedClass") + expect { + o.defined_instance_method + }.to fail_matching('Double "LoadedClass (instance)"') + end + + it 'only allows instance methods that exist to be stubbed' do + o = instance_double('LoadedClass', :defined_instance_method => 1) + expect(o.defined_instance_method).to eq(1) + + prevents { allow(o).to receive(:undefined_instance_method) } + prevents { allow(o).to receive(:defined_class_method) } + end + + it 'only allows instance methods that exist to be expected' do + o = instance_double('LoadedClass') + expect(o).to receive(:defined_instance_method) + o.defined_instance_method + + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_class_method) } + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_class_method) } + end + + it 'allows `send` to be stubbed if it is defined on the class' do + o = instance_double('LoadedClass') + allow(o).to receive(:send).and_return("received") + expect(o.send(:msg)).to eq("received") + end + + it 'gives a descriptive error message for NoMethodError' do + o = instance_double("LoadedClass") + expect { + o.defined_private_method + }.to raise_error(NoMethodError, + /Double "LoadedClass \(instance\)"/) + end + + it 'does not allow dynamic methods to be expected' do + # This isn't possible at the moment since an instance of the class + # would be required for the verification, and we only have the + # class itself. + # + # This spec exists as "negative" documentation of the absence of a + # feature, to highlight the asymmetry from class doubles (that do + # support this behaviour). + prevents { + instance_double('LoadedClass', :dynamic_instance_method => 1) + } + end + + it 'checks the arity of stubbed methods' do + o = instance_double('LoadedClass') + prevents { + expect(o).to receive(:defined_instance_method).with(:a) + } + end + + it 'checks that stubbed methods are invoked with the correct arity' do + o = instance_double('LoadedClass', :defined_instance_method => 25) + expect { + o.defined_instance_method(:a) + }.to raise_error(ArgumentError, + "Wrong number of arguments. Expected 0, got 1.") + end + + if required_kw_args_supported? + it 'allows keyword arguments' do + o = instance_double('LoadedClass', :kw_args_method => true) + expect(o.kw_args_method(1, :required_arg => 'something')).to eq(true) + end + + context 'for a method that only accepts keyword args' do + it 'allows hash matchers like `hash_including` to be used in place of the keywords arg hash' do + o = instance_double('LoadedClass') + expect(o).to receive(:kw_args_method). + with(1, hash_including(:required_arg => 1)) + o.kw_args_method(1, :required_arg => 1) + end + + it 'allows anything matcher to be used in place of the keywords arg hash' do + o = instance_double('LoadedClass') + expect(o).to receive(:kw_args_method).with(1, anything) + o.kw_args_method(1, :required_arg => 1) + end + + it 'still checks positional arguments when matchers used for keyword args' do + o = instance_double('LoadedClass') + prevents(/Expected 1, got 3/) { + expect(o).to receive(:kw_args_method). + with(1, 2, 3, hash_including(:required_arg => 1)) + } + end + + it 'does not allow matchers to be used in an actual method call' do + o = instance_double('LoadedClass') + matcher = hash_including(:required_arg => 1) + allow(o).to receive(:kw_args_method).with(1, matcher) + expect { + o.kw_args_method(1, matcher) + }.to raise_error(ArgumentError) + end + end + + context 'for a method that accepts a mix of optional keyword and positional args' do + it 'allows hash matchers like `hash_including` to be used in place of the keywords arg hash' do + o = instance_double('LoadedClass') + expect(o).to receive(:mixed_args_method).with(1, 2, hash_including(:optional_arg_1 => 1)) + o.mixed_args_method(1, 2, :optional_arg_1 => 1) + end + end + + it 'checks that stubbed methods with required keyword args are ' + + 'invoked with the required arguments' do + o = instance_double('LoadedClass', :kw_args_method => true) + expect { + o.kw_args_method(:optional_arg => 'something') + }.to raise_error(ArgumentError) + end + end + + it 'allows class to be specified by constant' do + o = instance_double(LoadedClass, :defined_instance_method => 1) + expect(o.defined_instance_method).to eq(1) + end + + context 'for null objects' do + let(:o) { instance_double('LoadedClass').as_null_object } + + it 'only allows defined methods' do + expect(o.defined_instance_method).to eq(o) + prevents { o.undefined_method } + prevents { o.send(:undefined_method) } + prevents { o.__send__(:undefined_method) } + end + + it "includes the double's name in a private method error" do + expect { + o.rand + }.to raise_error(NoMethodError, %r{private.*Double "LoadedClass \(instance\)"}) + end + + it 'reports what public messages it responds to accurately' do + expect(o.respond_to?(:defined_instance_method)).to be true + expect(o.respond_to?(:defined_instance_method, true)).to be true + expect(o.respond_to?(:defined_instance_method, false)).to be true + + expect(o.respond_to?(:undefined_method)).to be false + expect(o.respond_to?(:undefined_method, true)).to be false + expect(o.respond_to?(:undefined_method, false)).to be false + end + + it 'reports that it responds to defined private methods when the appropriate arg is passed' do + expect(o.respond_to?(:defined_private_method)).to be false + expect(o.respond_to?(:defined_private_method, true)).to be true + expect(o.respond_to?(:defined_private_method, false)).to be false + end + + if RUBY_VERSION.to_f < 2.0 # respond_to?(:protected_method) changed behavior in Ruby 2.0. + it 'reports that it responds to protected methods' do + expect(o.respond_to?(:defined_protected_method)).to be true + expect(o.respond_to?(:defined_protected_method, true)).to be true + expect(o.respond_to?(:defined_protected_method, false)).to be true + end + else + it 'reports that it responds to protected methods when the appropriate arg is passed' do + expect(o.respond_to?(:defined_protected_method)).to be false + expect(o.respond_to?(:defined_protected_method, true)).to be true + expect(o.respond_to?(:defined_protected_method, false)).to be false + end + end + end + end + end +end diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb new file mode 100644 index 000000000..e6f9822aa --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb @@ -0,0 +1,57 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec.describe 'An instance double with the doubled class not loaded' do + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = false + end + + it 'includes the double name in errors for unexpected messages' do + o = instance_double("NonLoadedClass") + expect { + o.foo + }.to fail_matching('Double "NonLoadedClass (instance)"') + end + + it 'allows any instance method to be stubbed' do + o = instance_double('NonloadedClass') + allow(o).to receive(:undefined_instance_method).with(:arg).and_return(true) + expect(o.undefined_instance_method(:arg)).to eq(true) + end + + it 'allows any instance method to be expected' do + o = instance_double("NonloadedClass") + + expect(o).to receive(:undefined_instance_method). + with(:arg). + and_return(true) + + expect(o.undefined_instance_method(:arg)).to eq(true) + end + + it 'handles classes that are materialized after mocking' do + stub_const "A::B", Object.new + o = instance_double "A", :undefined_instance_method => true + + expect(o.undefined_instance_method).to eq(true) + end + + context 'for null objects' do + let(:o) { instance_double('NonLoadedClass').as_null_object } + + it 'returns self from any message' do + expect(o.a.b.c).to be(o) + end + + it 'reports it responds to any message' do + expect(o.respond_to?(:a)).to be true + expect(o.respond_to?(:a, false)).to be true + expect(o.respond_to?(:a, true)).to be true + end + end + end + end +end diff --git a/spec/rspec/mocks/verifying_doubles/method_visibility_spec.rb b/spec/rspec/mocks/verifying_doubles/method_visibility_spec.rb new file mode 100644 index 000000000..e97524372 --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/method_visibility_spec.rb @@ -0,0 +1,129 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec.describe "Method visibility for verified doubles" do + include_context "with isolated configuration" + + before do + RSpec::Mocks.configuration.verify_doubled_constant_names = true + end + + context "for an instance double (when the class is loaded)" do + shared_examples "preserves method visibility" do |visibility| + method_name = :"defined_#{visibility}_method" + + it "can allow a #{visibility} instance method" do + o = instance_double('LoadedClass') + allow(o).to receive(method_name).and_return(3) + expect(o.send method_name).to eq(3) + end + + it "can expect a #{visibility} instance method" do + o = instance_double('LoadedClass') + expect(o).to receive(method_name) + o.send method_name + end + + it "preserves #{visibility} visibility when allowing a #{visibility} method" do + preserves_visibility(method_name, visibility) do + instance_double('LoadedClass').tap do |o| + allow(o).to receive(method_name) + end + end + end + + it "preserves #{visibility} visibility when expecting a #{visibility} method" do + preserves_visibility(method_name, visibility) do + instance_double('LoadedClass').tap do |o| + expect(o).to receive(method_name).at_least(:once) + o.send(method_name) # to satisfy the expectation + end + end + end + + it "preserves #{visibility} visibility on a null object" do + preserves_visibility(method_name, visibility) do + instance_double('LoadedClass').as_null_object + end + end + end + + include_examples "preserves method visibility", :private + include_examples "preserves method visibility", :protected + end + + context "for a class double (when the class is loaded)" do + shared_examples "preserves method visibility" do |visibility| + method_name = :"defined_#{visibility}_class_method" + + it "can allow a #{visibility} instance method" do + o = class_double('LoadedClass') + allow(o).to receive(method_name).and_return(3) + expect(o.send method_name).to eq(3) + end + + it "can expect a #{visibility} instance method" do + o = class_double('LoadedClass') + expect(o).to receive(method_name) + o.send method_name + end + + it "preserves #{visibility} visibility when allowing a #{visibility} method" do + preserves_visibility(method_name, visibility) do + class_double('LoadedClass').tap do |o| + allow(o).to receive(method_name) + end + end + end + + it "preserves #{visibility} visibility when expecting a #{visibility} method" do + preserves_visibility(method_name, visibility) do + class_double('LoadedClass').tap do |o| + expect(o).to receive(method_name).at_least(:once) + o.send(method_name) # to satisfy the expectation + end + end + end + + it "preserves #{visibility} visibility on a null object" do + preserves_visibility(method_name, visibility) do + class_double('LoadedClass').as_null_object + end + end + end + + include_examples "preserves method visibility", :private + include_examples "preserves method visibility", :protected + end + + def preserves_visibility(method_name, visibility) + double = yield + + expect { + # send bypasses visbility, so we use eval instead. + eval("double.#{method_name}") + }.to raise_error(NoMethodError, a_message_indicating_visibility_violation(method_name, visibility)) + + expect { double.send(method_name) }.not_to raise_error + expect { double.__send__(method_name) }.not_to raise_error + + unless double.null_object? + # Null object doubles use `method_missing` and so the singleton class + # doesn't know what methods are defined. + singleton_class = class << double; self; end + expect(singleton_class.send("#{visibility}_method_defined?", method_name)).to be true + end + end + + RSpec::Matchers.define :a_message_indicating_visibility_violation do |method_name, visibility| + match do |msg| + # This should NOT Be just `msg.match(visibility)` because the method being called + # has the visibility name in it. We want to ensure it's a message that ruby is + # stating is of the given visibility. + msg.match("#{visibility} ") && msg.match(method_name.to_s) + end + end + end + end +end diff --git a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb new file mode 100644 index 000000000..0738586cf --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb @@ -0,0 +1,61 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec.describe 'An object double' do + it 'replaces an unloaded constant' do + o = object_double("LoadedClass::NOINSTANCE").as_stubbed_const + + expect(LoadedClass::NOINSTANCE).to eq(o) + + expect(o).to receive(:undefined_instance_method) + o.undefined_instance_method + end + + it 'replaces a constant by name and verifies instances methods' do + o = object_double("LoadedClass::INSTANCE").as_stubbed_const + + expect(LoadedClass::INSTANCE).to eq(o) + + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_class_method) } + prevents { o.defined_instance_method } + + expect(o).to receive(:defined_instance_method) + o.defined_instance_method + expect(o).to receive(:defined_private_method) + o.send :defined_private_method + end + + it 'can create a double that matches the interface of any arbitrary object' do + o = object_double(LoadedClass.new) + + prevents { expect(o).to receive(:undefined_instance_method) } + prevents { expect(o).to receive(:defined_class_method) } + prevents { o.defined_instance_method } + + expect(o).to receive(:defined_instance_method) + o.defined_instance_method + expect(o).to receive(:defined_private_method) + o.send :defined_private_method + end + + it 'does not allow transferring constants to an object' do + expect { + object_double("LoadedClass::INSTANCE"). + as_stubbed_const(:transfer_nested_constants => true) + }.to raise_error(/Cannot transfer nested constants/) + end + + it 'does not allow as_stubbed_constant for real objects' do + expect { + object_double(LoadedClass.new).as_stubbed_const + }.to raise_error(/Can not perform constant replacement with an object/) + end + + it 'is not a module' do + expect(object_double("LoadedClass::INSTANCE")).to_not be_a(Module) + end + end + end +end diff --git a/spec/support/doubled_classes.rb b/spec/support/doubled_classes.rb new file mode 100644 index 000000000..bef6b2d52 --- /dev/null +++ b/spec/support/doubled_classes.rb @@ -0,0 +1,68 @@ +class LoadedClass + extend RSpec::Support::RubyFeatures + + M = :m + N = :n + INSTANCE = LoadedClass.new + + class << self + + def respond_to?(method_name, include_all = false) + return true if method_name == :dynamic_class_method + super + end + + def defined_class_method + end + + def send + # fake out! + end + + protected + + def defined_protected_class_method + end + + private + + def defined_private_class_method + end + + end + + def defined_instance_method + end + + if required_kw_args_supported? + # Need to eval this since it is invalid syntax on earlier rubies. + eval <<-RUBY + def kw_args_method(foo, optional_arg:'hello', required_arg:) + end + + def mixed_args_method(foo, bar, optional_arg_1:1, optional_arg_2:2) + end + RUBY + end + + def send(*) + end + + def respond_to?(method_name, include_all = false) + return true if method_name == :dynamic_instance_method + super + end + + class Nested; end + +protected + + def defined_protected_method + end + +private + + def defined_private_method + "wink wink ;)" + end +end From 683f9118f891e18ed4d39cbe3e4c0b9a3ac8f1e3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 27 Sep 2014 20:48:42 -0700 Subject: [PATCH 0722/1179] Make doc strings a bit more accurate. --- spec/rspec/mocks/verifying_doubles/object_double_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb index 0738586cf..b29ef30d1 100644 --- a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb @@ -3,7 +3,7 @@ module RSpec module Mocks RSpec.describe 'An object double' do - it 'replaces an unloaded constant' do + it 'can replace an unloaded constant' do o = object_double("LoadedClass::NOINSTANCE").as_stubbed_const expect(LoadedClass::NOINSTANCE).to eq(o) @@ -12,7 +12,7 @@ module Mocks o.undefined_instance_method end - it 'replaces a constant by name and verifies instances methods' do + it 'can replace a constant by name and verify instance methods' do o = object_double("LoadedClass::INSTANCE").as_stubbed_const expect(LoadedClass::INSTANCE).to eq(o) From 9ed0fe8a1593654aca71f0fb60e523ede2e401d8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 27 Sep 2014 21:07:00 -0700 Subject: [PATCH 0723/1179] Rewrite verifying message expectation specs. - Move them under `verified_doubles` sub directory. - Use more of an integration style rather than a mock-based unit style. - This is easier to work with; the other specs surprised me in that I had to explicitly expect the error generator not to receive a message in order to write a spec for the "allowed" case (since it was `as_null_object` it would not fail automatically). This surprised me when trying to add some new specs here. - This also fixes the spec for the `any_args` case. The old spec did not fail when I temporarily removed the special `any_args` handling, because the expected method for that example accepts 1 argument, and without the `any_args` special handling, it would naturally be treated as a single argument. --- .../expected_arg_verification_spec.rb | 81 +++++++++++++++++++ .../verifying_message_expecation_spec.rb | 67 --------------- spec/support/doubled_classes.rb | 6 ++ 3 files changed, 87 insertions(+), 67 deletions(-) create mode 100644 spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb delete mode 100644 spec/rspec/mocks/verifying_message_expecation_spec.rb diff --git a/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb b/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb new file mode 100644 index 000000000..cda40cd7f --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb @@ -0,0 +1,81 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec.describe 'Expected argument verification (when `#with` is called)' do + # Note: these specs here aren't meant to be exhaustive. The specs in + # rspec-support for the method signature verifier are. Here we are just + # covering the code paths within the `with` implementation, including + # the special handling for `any_args` and `no_args`. + context "when doubling an unloaded class" do + it 'allows any arguments' do + expect(defined?(UnloadedClass)).to be_falsey + dbl = instance_double("UnloadedClass") + + expect { + expect(dbl).to receive(:message).with(:foo, :bar, :bazz) + }.not_to raise_error + + reset dbl + end + end + + context "when doubling a loaded class" do + let(:dbl) { instance_double(LoadedClass) } + after { reset dbl } + + context "when `any_args` is used" do + it "skips verification" do + expect { + expect(dbl).to receive(:instance_method_with_two_args).with(any_args) + }.not_to raise_error + end + end + + context "when `no_args` is used" do + it "allows a method expectation on a method that accepts no arguments" do + expect(LoadedClass.instance_method(:defined_instance_method).arity).to eq(0) + + expect { + expect(dbl).to receive(:defined_instance_method).with(no_args) + }.not_to raise_error + end + + it "allows a method expectation on a method that has defaults for all arguments" do + expect { + expect(dbl).to receive(:instance_method_with_only_defaults).with(no_args) + }.not_to raise_error + end + + it "does not allow a method expectation on a method that has required arguments" do + expect { + expect(dbl).to receive(:instance_method_with_two_args).with(no_args) + }.to fail_with("Wrong number of arguments. Expected 2, got 0.") + end + end + + context "when a list of args is provided" do + it "allows a method expectation where the arity matches" do + expect { + expect(dbl).to receive(:instance_method_with_two_args).with(1, 2) + }.not_to raise_error + end + + it "does not allow a method expectation with an arity mismatch" do + expect { + expect(dbl).to receive(:instance_method_with_two_args).with(1, 2, 3) + }.to fail_with("Wrong number of arguments. Expected 2, got 3.") + end + end + + context "when `with` is called with no args" do + it "fails with an error suggesting the user use `no_args` instead" do + expect { + expect(dbl).to receive(:instance_method_with_two_args).with + }.to raise_error(ArgumentError, /no_args/) + end + end + end + end + end +end diff --git a/spec/rspec/mocks/verifying_message_expecation_spec.rb b/spec/rspec/mocks/verifying_message_expecation_spec.rb deleted file mode 100644 index cf9d5ad64..000000000 --- a/spec/rspec/mocks/verifying_message_expecation_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -module RSpec - module Mocks - RSpec.describe VerifyingMessageExpectation do - describe '#with' do - let(:error_generator) { double.as_null_object } - let(:string_module_reference) { DirectModuleReference.new(String) } - - subject { - null = double.as_null_object - - described_class.new(error_generator, null, null, null) - } - - describe 'when expected method is not loaded' do - it 'allows any arguments to be expected' do - subject.with(:foo, :bar) - end - end - - describe 'when arity match fails' do - it 'raises error' do - args = ["abc123", "xyz987"] - subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) - expect(error_generator).to receive(:raise_invalid_arguments_error). - with(instance_of(Support::LooseSignatureVerifier)) - - subject.with(*args) - end - end - - describe 'when called with arguments' do - it 'matches arity against the number of arguments' do - subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) - expect(error_generator).not_to receive(:raise_invalid_arguments_error) - - subject.with("abc123") - end - end - - describe 'when called with any arguments matcher' do - it 'does not try to match arity' do - subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) - subject.with(any_args) - end - end - - describe 'when called with no arguments matcher' do - it 'matches arity to 0' do - subject.method_reference = InstanceMethodReference.new(string_module_reference, :replace) - expect(error_generator).to receive(:raise_invalid_arguments_error). - with(instance_of(Support::LooseSignatureVerifier)) - - subject.with(no_args) - end - end - - describe 'when called with no arguments and no block' do - it 'raises' do - expect { - subject.with - }.to raise_error(ArgumentError) - end - end - end - end - end -end diff --git a/spec/support/doubled_classes.rb b/spec/support/doubled_classes.rb index bef6b2d52..1cba2ce28 100644 --- a/spec/support/doubled_classes.rb +++ b/spec/support/doubled_classes.rb @@ -34,6 +34,12 @@ def defined_private_class_method def defined_instance_method end + def instance_method_with_two_args(a, b) + end + + def instance_method_with_only_defaults(a=1, b=2) + end + if required_kw_args_supported? # Need to eval this since it is invalid syntax on earlier rubies. eval <<-RUBY From 59fbfd54b518235413c4e037f148f3222f11f481 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 28 Sep 2014 09:41:16 -0700 Subject: [PATCH 0724/1179] Improve wording of docstring. --- .../mocks/verifying_doubles/expected_arg_verification_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb b/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb index cda40cd7f..936ee7259 100644 --- a/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb @@ -55,7 +55,7 @@ module Mocks end context "when a list of args is provided" do - it "allows a method expectation where the arity matches" do + it "allows a method expectation when the arity matches" do expect { expect(dbl).to receive(:instance_method_with_two_args).with(1, 2) }.not_to raise_error From edb39a8018bc4782de3e33376d1864501ab4e7b9 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 1 Oct 2014 21:50:55 +1000 Subject: [PATCH 0725/1179] Correct error with have_received counts * add triangulation example for have_received error * rescue RSpec::Mocks::MockExpectationError when replaying expectations, we need to count them all. --- lib/rspec/mocks/proxy.rb | 8 +++++++- spec/rspec/mocks/matchers/have_received_spec.rb | 10 +++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index ce7a6dbc8..865ef0361 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -91,7 +91,13 @@ def replay_received_message_on(expectation, &block) @messages_received.each do |(actual_method_name, args, _)| next unless expectation.matches?(actual_method_name, *args) - expectation.invoke(nil) + # rubocop:disable Lint/HandleExceptions + begin + expectation.invoke(nil) + rescue RSpec::Mocks::MockExpectationError + # carry on, our expectation failure is handled later + end + # rubocop:enable Lint/HandleExceptions block.call(*args) if block end end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 141aab548..6f08bcf8a 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -151,10 +151,18 @@ module Mocks expect(dbl).to have_received(:expected_method).exactly(3).times end - it 'fails when the message was received more times' do + it 'fails when the message was received more times than expected' do + expect { + expect(dbl).to have_received(:expected_method).exactly(1).times + }.to raise_error(/expected: 1 time.*received: 3 times/m) expect { expect(dbl).to have_received(:expected_method).exactly(2).times }.to raise_error(/expected: 2 times.*received: 3 times/m) + expect { + dbl.expected_method + dbl.expected_method + expect(dbl).to have_received(:expected_method).exactly(2).times + }.to raise_error(/expected: 2 times.*received: 5 times/m) end it 'fails when the message was received fewer times' do From 4197003ebc61c41b17af6c5156dc4610e885df39 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 2 Oct 2014 07:41:36 +1000 Subject: [PATCH 0726/1179] Don't raise an expectation when counting in have_received --- lib/rspec/mocks/message_expectation.rb | 13 +++++++++---- lib/rspec/mocks/proxy.rb | 8 +------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 3d095a686..c34a3cbba 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -228,14 +228,19 @@ def matches?(message, *args) @message == message && @argument_list_matcher.args_match?(*args) end + # @private + def safe_invoke(parent_stub, *args, &block) + invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block) + end + # @private def invoke(parent_stub, *args, &block) - invoke_incrementing_actual_calls_by(1, parent_stub, *args, &block) + invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block) end # @private def invoke_without_incrementing_received_count(parent_stub, *args, &block) - invoke_incrementing_actual_calls_by(0, parent_stub, *args, &block) + invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block) end # @private @@ -504,10 +509,10 @@ def increase_actual_received_count! private - def invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block) + def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) args.unshift(orig_object) if yield_receiver_to_implementation_block? - if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count)) + if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count)) @actual_received_count += increment @failed_fast = true # args are the args we actually received, @argument_list_matcher is the diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 865ef0361..b936a3a62 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -91,13 +91,7 @@ def replay_received_message_on(expectation, &block) @messages_received.each do |(actual_method_name, args, _)| next unless expectation.matches?(actual_method_name, *args) - # rubocop:disable Lint/HandleExceptions - begin - expectation.invoke(nil) - rescue RSpec::Mocks::MockExpectationError - # carry on, our expectation failure is handled later - end - # rubocop:enable Lint/HandleExceptions + expectation.safe_invoke(nil) block.call(*args) if block end end From 79d8ad61ec77df8b8a801aa41f1233790e9f8336 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 2 Oct 2014 07:48:00 +1000 Subject: [PATCH 0727/1179] extend rubocop class definition --- .rubocop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 70794c629..d2bde5df3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,7 +2,7 @@ inherit_from: .rubocop_rspec_base.yml # This should go down over time. Style/ClassLength: - Max: 278 + Max: 279 # This should go down over time. Style/CyclomaticComplexity: From b3f49e8bc0b3aa24358340e0412edb963caf3426 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 2 Oct 2014 21:34:37 +1000 Subject: [PATCH 0728/1179] changelog for #793 [skip ci] --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3a3e12639..5e40495a8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,11 @@ Enhancements: * Improve error message caused when you use `allow(...)` with the `have_received` matcher. (Jon Rowe, #788). +Bug Fixes: + +* Correct received messages count when used with `have_received` matcher + (Jon Rowe, #793) + ### 3.1.1 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) From fe697fc70f545fade6c11360bd38006bf4d3685c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 2 Oct 2014 21:39:28 +1000 Subject: [PATCH 0729/1179] fixup changelog after #793 [skip ci] --- Changelog.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4385d65cb..2befa90cb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### 3.2.0 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...master) +Bug Fixes: + +* Correct received messages count when used with `have_received` matcher + (Jon Rowe, #793) + ### 3.1.2 / 2014-09-26 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...v3.1.2) @@ -10,11 +15,6 @@ Bug Fixes: `have_received` matcher (they are not intended to be used together and previously caused an odd internal failure in rspec-mocks). (Jon Rowe, #788). -Bug Fixes: - -* Correct received messages count when used with `have_received` matcher - (Jon Rowe, #793) - ### 3.1.1 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) From 5e6d687fa32d33ae200bd2646326e44dbe44db4f Mon Sep 17 00:00:00 2001 From: Piotr Jakubowski Date: Fri, 3 Oct 2014 12:49:14 +0200 Subject: [PATCH 0730/1179] Add receive_messages to README It took some digging to figure out that this method is available, so I guess it's good idea to have it in README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a1e0310be..8645df5c5 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,9 @@ rspec-mocks supports 3 forms for declaring method stubs: ```ruby allow(book).to receive(:title) { "The RSpec Book" } allow(book).to receive(:title).and_return("The RSpec Book") +allow(book).to receive_messages( + :title => "The RSpec Book", + :subtitle => "Behaviour-Driven Development with RSpec, Cucumber, and Friends") ``` You can also use this shortcut, which creates a test double and declares a From 944750b1cf79821fde289cea8106825702928fa3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Sep 2014 23:32:09 -0700 Subject: [PATCH 0731/1179] Treat `any_args` as an arg splat. This allows it to match an arbitrary number of arguments at any point in an arg list. Closes #707. --- Changelog.md | 5 ++ features/basics/expecting_messages.feature | 2 +- features/basics/spies.feature | 8 +- features/old_syntax/should_receive.feature | 2 +- features/outside_rspec/minitest.feature | 26 +++---- features/outside_rspec/standalone.feature | 8 +- lib/rspec/mocks/argument_list_matcher.rb | 32 ++++++-- lib/rspec/mocks/argument_matchers.rb | 2 +- spec/rspec/mocks/argument_matchers_spec.rb | 91 +++++++++++++++++++--- spec/rspec/mocks/partial_double_spec.rb | 2 +- 10 files changed, 137 insertions(+), 41 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2befa90cb..a29cd0469 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,11 @@ Bug Fixes: `have_received` matcher (they are not intended to be used together and previously caused an odd internal failure in rspec-mocks). (Jon Rowe, #788). +Enhancements: + +* Treat `any_args` as an arg splat, allowing it to match an arbitrary + number of args at any point in an arg list. (Myron Marston, #786) + ### 3.1.1 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) diff --git a/features/basics/expecting_messages.feature b/features/basics/expecting_messages.feature index f0ff91c02..e885f9509 100644 --- a/features/basics/expecting_messages.feature +++ b/features/basics/expecting_messages.feature @@ -19,7 +19,7 @@ Feature: Expecting messages """ 1) An unfulfilled positive message expectation triggers a failure Failure/Error: expect(dbl).to receive(:foo) - (Double "Some Collaborator").foo(any args) + (Double "Some Collaborator").foo(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments """ diff --git a/features/basics/spies.feature b/features/basics/spies.feature index 195362a04..275333f72 100644 --- a/features/basics/spies.feature +++ b/features/basics/spies.feature @@ -70,7 +70,7 @@ Feature: Spies """ 1) failure when the message has not been received for a spy Failure/Error: expect(invitation).to have_received(:deliver) - (Double "invitation").deliver(any args) + (Double "invitation").deliver(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments """ @@ -78,7 +78,7 @@ Feature: Spies """ 2) failure when the message has not been received for a partial double Failure/Error: expect(Invitation).to have_received(:deliver) - ().deliver(any args) + ().deliver(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments """ @@ -119,7 +119,7 @@ Feature: Spies | | | 1) An invitiation fails when a count constraint is not satisfied | | Failure/Error: expect(invitation).to have_received(:deliver).at_least(3).times | - | (Double "invitation").deliver(any args) | + | (Double "invitation").deliver(*(any args)) | | expected: at least 3 times with any arguments | | received: 2 times with any arguments | | | @@ -140,5 +140,5 @@ Feature: Spies Then it should pass with: """ An invitation - should have received deliver(any args) 1 time + should have received deliver(*(any args)) 1 time """ diff --git a/features/old_syntax/should_receive.feature b/features/old_syntax/should_receive.feature index 1ded4d97b..5e7b4d95d 100644 --- a/features/old_syntax/should_receive.feature +++ b/features/old_syntax/should_receive.feature @@ -36,7 +36,7 @@ Feature: `should_receive` """ 1) An unfulfilled message expectation triggers a failure Failure/Error: dbl.should_receive(:foo) - (Double "Some Collaborator").foo(any args) + (Double "Some Collaborator").foo(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments """ diff --git a/features/outside_rspec/minitest.feature b/features/outside_rspec/minitest.feature index 5520db60c..8e43bd779 100644 --- a/features/outside_rspec/minitest.feature +++ b/features/outside_rspec/minitest.feature @@ -65,16 +65,16 @@ Feature: Integrate with Minitest """ When I run `ruby -Itest test/rspec_mocks_test.rb` Then it should fail with the following output: - | 1) Error: | - | RSpecMocksTest#test_failing_negative_expectation: | - | RSpec::Mocks::MockExpectationError: (Double).message(no args) | - | expected: 0 times with any arguments | - | received: 1 time | - | | - | 2) Error: | - | RSpecMocksTest#test_failing_positive_expectation: | - | RSpec::Mocks::MockExpectationError: (Double).message(any args) | - | expected: 1 time with any arguments | - | received: 0 times with any arguments | - | | - | 4 runs, 0 assertions, 0 failures, 2 errors, 0 skips | + | 1) Error: | + | RSpecMocksTest#test_failing_negative_expectation: | + | RSpec::Mocks::MockExpectationError: (Double).message(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 2) Error: | + | RSpecMocksTest#test_failing_positive_expectation: | + | RSpec::Mocks::MockExpectationError: (Double).message(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 4 runs, 0 assertions, 0 failures, 2 errors, 0 skips | diff --git a/features/outside_rspec/standalone.feature b/features/outside_rspec/standalone.feature index 679c9e3d3..c324e27d3 100644 --- a/features/outside_rspec/standalone.feature +++ b/features/outside_rspec/standalone.feature @@ -27,7 +27,7 @@ Feature: Standalone """ When I run `ruby example.rb` Then it should fail with the following output: - | (Double "greeter").say_hi(any args) | - | RSpec::Mocks::MockExpectationError | - | expected: 1 time with any arguments | - | received: 0 times with any arguments | + | (Double "greeter").say_hi(*(any args)) | + | RSpec::Mocks::MockExpectationError | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index a7d15ea5e..7d4a389e2 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -44,12 +44,6 @@ class ArgumentListMatcher # @see #args_match? def initialize(*expected_args) @expected_args = expected_args - - @matchers = case expected_args.first - when ArgumentMatchers::AnyArgsMatcher then Array - when ArgumentMatchers::NoArgsMatcher then [] - else expected_args - end end # @api public @@ -60,13 +54,37 @@ def initialize(*expected_args) # # @see #initialize def args_match?(*args) - Support::FuzzyMatcher.values_match?(@matchers, args) + Support::FuzzyMatcher.values_match?(matchers_for(args), args) end # Value that will match all argument lists. # # @private MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher.new) + + # Singleton instance of AnyArgMatcher to save on memory. + # It's immutable and thus safe to re-use many times. + # @private + ANYTHING = ArgumentMatchers::AnyArgMatcher.new + + private + + def matchers_for(actual_args) + return [] if expected_args.one? && ArgumentMatchers::NoArgsMatcher === expected_args.first + + any_args_index = expected_args.index { |arg| ArgumentMatchers::AnyArgsMatcher === arg } + return expected_args unless any_args_index + + replace_any_args_with_splat_of_anything(any_args_index, actual_args.count) + end + + def replace_any_args_with_splat_of_anything(before_count, actual_args_count) + any_args_count = actual_args_count - expected_args.count + 1 + after_count = expected_args.count - before_count - 1 + + any_args = 1.upto(any_args_count).map { ANYTHING } + expected_args.first(before_count) + any_args + expected_args.last(after_count) + end end end end diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 9194e2939..e34ea0965 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -129,7 +129,7 @@ def self.anythingize_lonely_keys(*args) # @private class AnyArgsMatcher def description - "any args" + "*(any args)" end end diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index c8b766ca1..6ecac474a 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -129,19 +129,92 @@ module Mocks end describe "any_args" do - it "matches no args against any_args" do - expect(a_double).to receive(:random_call).with(any_args) - a_double.random_call + context "as the only arg passed to `with`" do + before { expect(a_double).to receive(:random_call).with(any_args) } + + it "matches no args" do + a_double.random_call + end + + it "matches one arg" do + a_double.random_call("a string") + end + + it "matches many args" do + a_double.random_call("a string", :other, 3) + end end - it "matches one arg against any_args" do - expect(a_double).to receive(:random_call).with(any_args) - a_double.random_call("a string") + context "as the last of three args" do + before { expect(a_double).to receive(:random_call).with(1, /foo/, any_args) } + + it "matches a call of two args when it matches the first two explicit args" do + a_double.random_call(1, "food") + end + + it "matches a call of three args when it matches the first two explicit args" do + a_double.random_call(1, "food", :more) + end + + it "matches a call of four args when it matches the first two explicit args" do + a_double.random_call(1, "food", :more, :args) + end + + it "does not match a call where the first two args do not match", :reset => true do + expect { a_double.random_call(1, "bar", 2, 3) }.to fail_matching "expected: (1, /foo/, *(any args))" + end + + it "does not match a call of no args", :reset => true do + expect { a_double.random_call }.to fail_matching "expected: (1, /foo/, *(any args))" + end end - it "handles non matching instances nicely", :reset => true do - expect(a_double).to receive(:random_call).with(1, any_args) - expect { a_double.random_call }.to fail_matching "expected: (1, any args)" + context "as the first of three args" do + before { expect(a_double).to receive(:random_call).with(any_args, 1, /foo/) } + + it "matches a call of two args when it matches the last two explicit args" do + a_double.random_call(1, "food") + end + + it "matches a call of three args when it matches the last two explicit args" do + a_double.random_call(nil, 1, "food") + end + + it "matches a call of four args when it matches the last two explicit args" do + a_double.random_call(:some, :args, 1, "food") + end + + it "does not match a call where the last two args do not match", :reset => true do + expect { a_double.random_call(1, "bar", 2, 3) }.to fail_matching "expected: (*(any args), 1, /foo/)" + end + + it "does not match a call of no args", :reset => true do + expect { a_double.random_call }.to fail_matching "expected: (*(any args), 1, /foo/)" + end + end + + context "as the middle of three args" do + before { expect(a_double).to receive(:random_call).with(1, any_args, /foo/) } + + it "matches a call of two args when it matches the first and last args" do + a_double.random_call(1, "food") + end + + it "matches a call of three args when it matches the first and last args" do + a_double.random_call(1, nil, "food") + end + + it "matches a call of four args when it matches the first and last args" do + a_double.random_call(1, :some, :args, "food") + end + + it "does not match a call where the first and last args do not match", :reset => true do + expect { a_double.random_call(nil, "bar", 2, 3) }.to fail_matching "expected: (1, *(any args), /foo/)" + end + + it "does not match a call of no args", :reset => true do + expect { a_double.random_call }.to fail_matching "expected: (1, *(any args), /foo/)" + end end end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 57b2ad55c..46f7d526f 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -102,7 +102,7 @@ module Mocks verify _nil }.to raise_error( RSpec::Mocks::MockExpectationError, - %Q|(nil).foobar(any args)\n expected: 1 time with any arguments\n received: 0 times with any arguments| + %Q|(nil).foobar(*(any args))\n expected: 1 time with any arguments\n received: 0 times with any arguments| ) end From 903ebe5ebf2c8bcf1f738dec84316bb62b195cd1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Sep 2014 13:57:31 -0700 Subject: [PATCH 0732/1179] Convert stateless arg matchers to singletons. This saves on memory (fewer objects to GC!). --- lib/rspec/mocks/argument_list_matcher.rb | 13 +++------ lib/rspec/mocks/argument_matchers.rb | 36 ++++++++++++++++++------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index 7d4a389e2..fd72dca9d 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -60,19 +60,14 @@ def args_match?(*args) # Value that will match all argument lists. # # @private - MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher.new) - - # Singleton instance of AnyArgMatcher to save on memory. - # It's immutable and thus safe to re-use many times. - # @private - ANYTHING = ArgumentMatchers::AnyArgMatcher.new + MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher::INSTANCE) private def matchers_for(actual_args) - return [] if expected_args.one? && ArgumentMatchers::NoArgsMatcher === expected_args.first + return [] if [ArgumentMatchers::NoArgsMatcher::INSTANCE] == expected_args - any_args_index = expected_args.index { |arg| ArgumentMatchers::AnyArgsMatcher === arg } + any_args_index = expected_args.index(ArgumentMatchers::AnyArgsMatcher::INSTANCE) return expected_args unless any_args_index replace_any_args_with_splat_of_anything(any_args_index, actual_args.count) @@ -82,7 +77,7 @@ def replace_any_args_with_splat_of_anything(before_count, actual_args_count) any_args_count = actual_args_count - expected_args.count + 1 after_count = expected_args.count - before_count - 1 - any_args = 1.upto(any_args_count).map { ANYTHING } + any_args = 1.upto(any_args_count).map { ArgumentMatchers::AnyArgMatcher::INSTANCE } expected_args.first(before_count) + any_args + expected_args.last(after_count) end end diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index e34ea0965..7e08054f6 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -21,7 +21,7 @@ module ArgumentMatchers # # expect(object).to receive(:message).with(any_args) def any_args - AnyArgsMatcher.new + AnyArgsMatcher::INSTANCE end # Matches any argument at all. @@ -30,7 +30,7 @@ def any_args # # expect(object).to receive(:message).with(anything) def anything - AnyArgMatcher.new + AnyArgMatcher::INSTANCE end # Matches no arguments. @@ -39,7 +39,7 @@ def anything # # expect(object).to receive(:message).with(no_args) def no_args - NoArgsMatcher.new + NoArgsMatcher::INSTANCE end # Matches if the actual argument responds to the specified messages. @@ -58,7 +58,7 @@ def duck_type(*args) # # expect(object).to receive(:message).with(boolean()) def boolean - BooleanMatcher.new + BooleanMatcher::INSTANCE end # Matches a hash that includes the specified key(s) or key/value pairs. @@ -122,19 +122,37 @@ def kind_of(klass) # @private def self.anythingize_lonely_keys(*args) hash = args.last.class == Hash ? args.delete_at(-1) : {} - args.each { | arg | hash[arg] = AnyArgMatcher.new } + args.each { | arg | hash[arg] = AnyArgMatcher::INSTANCE } hash end + # Intended to be subclassed by stateless, immutable argument matchers. + # Provides a `::INSTANCE` constant for accessing a + # global singleton instance of the matcher. This saves on memory a bit + # (as their is no need to construct multiple instance since there is + # no internal instance state). It also facilities the special case logic + # we need for some of these matchers, by making it easy to do comparisons + # like: `[klass::INSTANCE] == args` rather than + # `args.count == 1 && klass === args.first`. + # + # @private + class SingletonMatcher + private_class_method :new + + def self.inherited(subklass) + subklass.const_set(:INSTANCE, subklass.send(:new)) + end + end + # @private - class AnyArgsMatcher + class AnyArgsMatcher < SingletonMatcher def description "*(any args)" end end # @private - class AnyArgMatcher + class AnyArgMatcher < SingletonMatcher def ===(_other) true end @@ -145,14 +163,14 @@ def description end # @private - class NoArgsMatcher + class NoArgsMatcher < SingletonMatcher def description "no args" end end # @private - class BooleanMatcher + class BooleanMatcher < SingletonMatcher def ===(value) true == value || false == value end From 80f6f622800ecebb39836b2e6feda7c07d6286ad Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Sep 2014 21:24:18 -0700 Subject: [PATCH 0733/1179] Provide a clear failure when invalid args are passed to `with`. --- Changelog.md | 9 +++++++++ lib/rspec/mocks/argument_list_matcher.rb | 22 +++++++++++++++++----- spec/rspec/mocks/argument_matchers_spec.rb | 16 ++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index a29cd0469..b6ebe466d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,15 @@ Enhancements: * Treat `any_args` as an arg splat, allowing it to match an arbitrary number of args at any point in an arg list. (Myron Marston, #786) +Bug Fixes: + +* Provide a clear error when users wrongly combine `no_args` with + additional arguments (e.g. `expect().to receive().with(no_args, 1)`). + (Myron Marston, #786) +* Provide a clear error when users wrongly use `any_args` multiple times in the + same argument list (e.g. `expect().to receive().with(any_args, 1, any_args)`. + (Myron Marston, #786) + ### 3.1.1 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index fd72dca9d..ba24c22b0 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -44,6 +44,7 @@ class ArgumentListMatcher # @see #args_match? def initialize(*expected_args) @expected_args = expected_args + ensure_expected_args_valid! end # @api public @@ -57,11 +58,6 @@ def args_match?(*args) Support::FuzzyMatcher.values_match?(matchers_for(args), args) end - # Value that will match all argument lists. - # - # @private - MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher::INSTANCE) - private def matchers_for(actual_args) @@ -80,6 +76,22 @@ def replace_any_args_with_splat_of_anything(before_count, actual_args_count) any_args = 1.upto(any_args_count).map { ArgumentMatchers::AnyArgMatcher::INSTANCE } expected_args.first(before_count) + any_args + expected_args.last(after_count) end + + def ensure_expected_args_valid! + if expected_args.count(ArgumentMatchers::AnyArgsMatcher::INSTANCE) > 1 + raise ArgumentError, "`any_args` can only be passed to " \ + "`with` once but you have passed it multiple times." + elsif expected_args.count > 1 && expected_args.include?(ArgumentMatchers::NoArgsMatcher::INSTANCE) + raise ArgumentError, "`no_args` can only be passed as a " \ + "singleton argument to `with` (i.e. `with(no_args)`), " \ + "but you have passed additional arguments." + end + end + + # Value that will match all argument lists. + # + # @private + MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher::INSTANCE) end end end diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index 6ecac474a..4e1e41461 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -216,6 +216,14 @@ module Mocks expect { a_double.random_call }.to fail_matching "expected: (1, *(any args), /foo/)" end end + + context "when passed twice" do + it 'immediately signals that this is invalid', :reset => true do + expect { + expect(a_double).to receive(:random_call).with(any_args, 1, any_args) + }.to raise_error(ArgumentError, /any_args/) + end + end end describe "no_args" do @@ -228,6 +236,14 @@ module Mocks expect(a_double).to receive(:msg).with(no_args) expect { a_double.msg(37) }.to fail_matching "expected: (no args)" end + + context "when passed with other arguments" do + it 'immediately signals that this is invalid', :reset => true do + expect { + expect(a_double).to receive(:random_call).with(no_args, 3) + }.to raise_error(ArgumentError, /no_args/) + end + end end describe "hash_including" do From a6eff993926e3a58e32574e66b0e65606e8ccd88 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 29 Sep 2014 07:55:20 -0700 Subject: [PATCH 0734/1179] Fix `with` verified double verification to work with new `any_args` semantics. --- lib/rspec/mocks/argument_list_matcher.rb | 11 ++-- .../mocks/verifying_message_expecation.rb | 26 +++------ .../expected_arg_verification_spec.rb | 55 +++++++++++++++++-- 3 files changed, 67 insertions(+), 25 deletions(-) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index ba24c22b0..453bf9f11 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -55,12 +55,13 @@ def initialize(*expected_args) # # @see #initialize def args_match?(*args) - Support::FuzzyMatcher.values_match?(matchers_for(args), args) + Support::FuzzyMatcher.values_match?(resolve_expected_args_based_on(args), args) end - private - - def matchers_for(actual_args) + # @private + # Resolves abstract arg placeholders like `no_args` and `any_args` into + # a more concrete arg list based on the provided `actual_args`. + def resolve_expected_args_based_on(actual_args) return [] if [ArgumentMatchers::NoArgsMatcher::INSTANCE] == expected_args any_args_index = expected_args.index(ArgumentMatchers::AnyArgsMatcher::INSTANCE) @@ -69,6 +70,8 @@ def matchers_for(actual_args) replace_any_args_with_splat_of_anything(any_args_index, actual_args.count) end + private + def replace_any_args_with_splat_of_anything(before_count, actual_args_count) any_args_count = actual_args_count - expected_args.count + 1 after_count = expected_args.count - before_count - 1 diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expecation.rb index 6acdf8090..c81ef322b 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expecation.rb @@ -23,31 +23,23 @@ def initialize(*args) # @private def with(*args, &block) - unless ArgumentMatchers::AnyArgsMatcher === args.first - expected_args = if ArgumentMatchers::NoArgsMatcher === args.first - [] - elsif args.length > 0 - args - else - # No arguments given, this will raise. - super - end - - validate_expected_arguments!(expected_args) + super(*args, &block).tap do + validate_expected_arguments! do |signature| + example_call_site_args = [:an_arg] * signature.min_non_kw_args + example_call_site_args << :kw_args_hash if signature.required_kw_args.any? + @argument_list_matcher.resolve_expected_args_based_on(example_call_site_args) + end end - super end private - def validate_expected_arguments!(actual_args) + def validate_expected_arguments! return if method_reference.nil? method_reference.with_signature do |signature| - verifier = Support::LooseSignatureVerifier.new( - signature, - actual_args - ) + args = yield signature + verifier = Support::LooseSignatureVerifier.new(signature, args) unless verifier.valid? # Fail fast is required, otherwise the message expecation will fail diff --git a/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb b/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb index 936ee7259..9baee520e 100644 --- a/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb @@ -25,10 +25,32 @@ module Mocks after { reset dbl } context "when `any_args` is used" do - it "skips verification" do - expect { - expect(dbl).to receive(:instance_method_with_two_args).with(any_args) - }.not_to raise_error + context "as the only argument" do + it "is allowed regardless of how many args the method requires" do + expect { + expect(dbl).to receive(:instance_method_with_two_args).with(any_args) + }.not_to raise_error + end + end + + context "as the first argument, with too many additional args" do + it "is disallowed" do + expect { + expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1, 2, 3) + }.to fail_with("Wrong number of arguments. Expected 2, got 3.") + end + end + + context "as the first argument, with an allowed number of additional args" do + it "is allowed" do + expect { + expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1, 2) + }.not_to raise_error + + expect { + expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1) + }.not_to raise_error + end end end @@ -52,6 +74,31 @@ module Mocks expect(dbl).to receive(:instance_method_with_two_args).with(no_args) }.to fail_with("Wrong number of arguments. Expected 2, got 0.") end + + if RSpec::Support::RubyFeatures.required_kw_args_supported? + context "for a method with required keyword args" do + it 'covers the required args when `any_args` is last' do + expect { + expect(dbl).to receive(:kw_args_method).with(1, any_args) + }.not_to raise_error + end + + it 'does not cover the required args when there are args after `any_args`' do + expect { + # Use eval to avoid syntax error on 1.8 and 1.9 + eval("expect(dbl).to receive(:kw_args_method).with(any_args, optional_arg: 3)") + }.to fail_with("Missing required keyword arguments: required_arg") + end + end + end + end + + if RSpec::Support::RubyFeatures.required_kw_args_supported? + it 'does not cover required args when `any_args` is not used' do + expect { + expect(dbl).to receive(:kw_args_method).with(anything) + }.to fail_with("Missing required keyword arguments: required_arg") + end end context "when a list of args is provided" do From c3c94fa5b9606e528bd15772260312759f7e7f9b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Sep 2014 09:57:35 -0700 Subject: [PATCH 0735/1179] Update `any_args` docs to explain splat semantics. --- README.md | 5 +++-- .../setting_constraints/matching_arguments.feature | 1 + lib/rspec/mocks/argument_matchers.rb | 10 +++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8645df5c5..c87d48867 100644 --- a/README.md +++ b/README.md @@ -206,8 +206,9 @@ rspec-mocks also adds some keyword Symbols that you can use to specify certain kinds of arguments: ```ruby -expect(double).to receive(:msg).with(no_args()) -expect(double).to receive(:msg).with(any_args()) +expect(double).to receive(:msg).with(no_args) +expect(double).to receive(:msg).with(any_args) +expect(double).to receive(:msg).with(1, any_args) # any args acts like an arg splat and can go anywhere expect(double).to receive(:msg).with(1, kind_of(Numeric), "b") #2nd argument can be any kind of Numeric expect(double).to receive(:msg).with(1, boolean(), "b") #2nd argument can be true or false expect(double).to receive(:msg).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp diff --git a/features/setting_constraints/matching_arguments.feature b/features/setting_constraints/matching_arguments.feature index 269bc029e..638a2a368 100644 --- a/features/setting_constraints/matching_arguments.feature +++ b/features/setting_constraints/matching_arguments.feature @@ -9,6 +9,7 @@ Feature: Matching arguments | Literal arguments | `with(1, true)` | `foo(1, true)` | | Anything that supports case equality (`===`) | `with(/bar/)` | `foo("barn")` | | Any list of args | `with(any_args)` | `foo()`
`foo(1)`
`foo(:bar, 2)` | + | Any sublist of args (like an arg splat) | `with(1, any_args)` | `foo(1)`
`foo(1, :bar, :bazz)` | | An empty list of args | `with(no_args)` | `foo()` | | Anything for a given positional arg | `with(3, anything)` | `foo(3, nil)`
`foo(3, :bar)` | | Against an interface | `with(duck_type(:each))` | `foo([])` | diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 7e08054f6..56052ce76 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -14,12 +14,16 @@ module Mocks # # @see ArgumentListMatcher module ArgumentMatchers - # Matches any args at all. Supports a more explicit variation of - # `expect(object).to receive(:message)` + # Acts like an arg splat, matching any number of args at any point in an arg list. # # @example # - # expect(object).to receive(:message).with(any_args) + # expect(object).to receive(:message).with(1, 2, any_args) + # + # # matches any of these: + # object.message(1, 2) + # object.message(1, 2, 3) + # object.message(1, 2, 3, 4) def any_args AnyArgsMatcher::INSTANCE end From 1cbb1c60fbe25016d2cf851814a20649ef8d7372 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 5 Oct 2014 15:42:32 -0700 Subject: [PATCH 0736/1179] =?UTF-8?q?Simplify=20wording=20based=20on=20@xa?= =?UTF-8?q?viershay=E2=80=99s=20suggestion.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/rspec/mocks/argument_matchers.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 56052ce76..3e4a4ccea 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -131,12 +131,11 @@ def self.anythingize_lonely_keys(*args) end # Intended to be subclassed by stateless, immutable argument matchers. - # Provides a `::INSTANCE` constant for accessing a - # global singleton instance of the matcher. This saves on memory a bit - # (as their is no need to construct multiple instance since there is - # no internal instance state). It also facilities the special case logic - # we need for some of these matchers, by making it easy to do comparisons - # like: `[klass::INSTANCE] == args` rather than + # Provides a `::INSTANCE` constant for accessing a global + # singleton instance of the matcher. There is no need to construct + # multiple instance since there is no state. It also facilities the + # special case logic we need for some of these matchers, by making it + # easy to do comparisons like: `[klass::INSTANCE] == args` rather than # `args.count == 1 && klass === args.first`. # # @private From dcfb9bb2bde23b02bd4727e3e1534d98de984259 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 5 Oct 2014 15:31:53 +1100 Subject: [PATCH 0737/1179] explictly deny any_instance_of with have_received --- lib/rspec/mocks/matchers/have_received.rb | 18 +++++++++++++++--- .../rspec/mocks/matchers/have_received_spec.rb | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 2a1ee261a..0534ab850 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -55,13 +55,25 @@ def description end def setup_allowance(_subject, &_block) - raise RSpec::Mocks::MockExpectationError, - "Using allow(...) with the `have_received` matcher is not " \ - "supported as it would have no effect." + disallow("allow", " as it would have no effect") + end + + def setup_any_instance_allowance(_subject, &_block) + disallow("allow_any_instance_of") + end + + def setup_any_instance_expectation(_subject, &_block) + disallow("expect_any_instance_of") end private + def disallow(type, reason="") + raise RSpec::Mocks::MockExpectationError, + "Using #{type}(...) with the `have_received` " \ + "matcher is not supported#{reason}." + end + def expect @expectation ||= begin expectation = mock_proxy.build_expectation(@method_name) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 6f08bcf8a..b256f59a8 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -475,6 +475,22 @@ module Mocks end end + describe "allow_any_instance_of(...).to have_received" do + it "fails because its nonsensical" do + expect { + allow_any_instance_of(double).to have_received(:some_method) + }.to fail_with("Using allow_any_instance_of(...) with the `have_received` matcher is not supported.") + end + end + + describe "expect_any_instance_of(...).to have_received" do + it "fails because we dont want to support it" do + expect { + expect_any_instance_of(double).to have_received(:some_method) + }.to fail_with("Using expect_any_instance_of(...) with the `have_received` matcher is not supported.") + end + end + def double_with_met_expectation(method_name, *args) double = double_with_unmet_expectation(method_name) meet_expectation(double, method_name, *args) From 9664eaf462aa4bb7a09b8e8bb7d7a23574d820b0 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 5 Oct 2014 15:43:59 +1100 Subject: [PATCH 0738/1179] changelog for #799 [skip ci] --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 2befa90cb..12c0dafe9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,10 @@ Bug Fixes: * Correct received messages count when used with `have_received` matcher (Jon Rowe, #793) +* Provide a clear error message when you use `allow_any_instance_of(...)` or + `expect_any_instance_of(...)` with the `have_received` matcher (they are + not intended to be used together and previously caused an odd internal + failure in rspec-mocks). (Jon Rowe, #799). ### 3.1.2 / 2014-09-26 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...v3.1.2) From 3bd71e878389686ecbeab923e93dc5e2fd9eb869 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Oct 2014 09:49:11 -0700 Subject: [PATCH 0739/1179] Fix changelog. Somehow the changlog entries from #786 wound up under the wrong section. [ci skip] --- Changelog.md | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/Changelog.md b/Changelog.md index 500464fb4..7df196560 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,14 +1,25 @@ ### 3.2.0 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...master) +Enhancements: + +* Treat `any_args` as an arg splat, allowing it to match an arbitrary + number of args at any point in an arg list. (Myron Marston, #786) + Bug Fixes: -* Correct received messages count when used with `have_received` matcher +* Correct received messages count when used with `have_received` matcher. (Jon Rowe, #793) * Provide a clear error message when you use `allow_any_instance_of(...)` or `expect_any_instance_of(...)` with the `have_received` matcher (they are not intended to be used together and previously caused an odd internal - failure in rspec-mocks). (Jon Rowe, #799). + failure in rspec-mocks). (Jon Rowe, #799) +* Provide a clear error when users wrongly combine `no_args` with + additional arguments (e.g. `expect().to receive().with(no_args, 1)`). + (Myron Marston, #786) +* Provide a clear error when users wrongly use `any_args` multiple times in the + same argument list (e.g. `expect().to receive().with(any_args, 1, any_args)`. + (Myron Marston, #786) ### 3.1.2 / 2014-09-26 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...v3.1.2) @@ -19,20 +30,6 @@ Bug Fixes: `have_received` matcher (they are not intended to be used together and previously caused an odd internal failure in rspec-mocks). (Jon Rowe, #788). -Enhancements: - -* Treat `any_args` as an arg splat, allowing it to match an arbitrary - number of args at any point in an arg list. (Myron Marston, #786) - -Bug Fixes: - -* Provide a clear error when users wrongly combine `no_args` with - additional arguments (e.g. `expect().to receive().with(no_args, 1)`). - (Myron Marston, #786) -* Provide a clear error when users wrongly use `any_args` multiple times in the - same argument list (e.g. `expect().to receive().with(any_args, 1, any_args)`. - (Myron Marston, #786) - ### 3.1.1 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) From d8a7c28c4fe035b7e2d86b765fcfc1d3175beac4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 8 Oct 2014 22:06:31 -0700 Subject: [PATCH 0740/1179] Fix verified double `with` verification for method stubs. --- Changelog.md | 2 ++ lib/rspec/mocks/verifying_proxy.rb | 5 +++++ .../class_double_with_class_loaded_spec.rb | 7 +++++++ .../instance_double_with_class_loaded_spec.rb | 7 +++++++ spec/rspec/mocks/verifying_doubles/object_double_spec.rb | 7 +++++++ 5 files changed, 28 insertions(+) diff --git a/Changelog.md b/Changelog.md index 7df196560..4de39430c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,8 @@ Bug Fixes: `expect_any_instance_of(...)` with the `have_received` matcher (they are not intended to be used together and previously caused an odd internal failure in rspec-mocks). (Jon Rowe, #799) +* Fix verified double `with` verification so that it applies to method + stubs. (Myron Marston, #790) * Provide a clear error when users wrongly combine `no_args` with additional arguments (e.g. `expect().to receive().with(no_args, 1)`). (Myron Marston, #786) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 44ed76d6c..9dd9fd207 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -121,6 +121,11 @@ def add_expectation(*args, &block) super(*args, &block).tap { |x| x.method_reference = @method_reference } end + def add_stub(*args, &block) + # explict params necessary for 1.8.7 see #626 + super(*args, &block).tap { |x| x.method_reference = @method_reference } + end + def proxy_method_invoked(obj, *args, &block) validate_arguments!(args) super diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index 9c85c8b0d..b10a2dc55 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -106,6 +106,13 @@ module Mocks expect(o.defined_class_method).to eq(o) prevents { o.undefined_method } end + + it 'validates `with` args against the method signature when stubbing a method' do + dbl = class_double(LoadedClass) + prevents(/Wrong number of arguments. Expected 0, got 2./) { + allow(dbl).to receive(:defined_class_method).with(2, :args) + } + end end end end diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index edb269bdd..5fb4342df 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -132,6 +132,13 @@ module Mocks end end + it 'validates `with` args against the method signature when stubbing a method' do + dbl = instance_double(LoadedClass) + prevents(/Wrong number of arguments. Expected 2, got 3./) { + allow(dbl).to receive(:instance_method_with_two_args).with(3, :foo, :args) + } + end + it 'allows class to be specified by constant' do o = instance_double(LoadedClass, :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) diff --git a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb index b29ef30d1..c26cb7b35 100644 --- a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb @@ -56,6 +56,13 @@ module Mocks it 'is not a module' do expect(object_double("LoadedClass::INSTANCE")).to_not be_a(Module) end + + it 'validates `with` args against the method signature when stubbing a method' do + dbl = object_double(LoadedClass.new) + prevents(/Wrong number of arguments. Expected 2, got 3./) { + allow(dbl).to receive(:instance_method_with_two_args).with(3, :foo, :args) + } + end end end end From 883d2a6239adf612d4b80fd2ee1808e1c2b6614b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 8 Oct 2014 23:42:26 -0700 Subject: [PATCH 0741/1179] Forwardport 3.1.3 release notes. [ci skip] --- Changelog.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4de39430c..898a576c7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...master) Enhancements: @@ -8,20 +8,26 @@ Enhancements: Bug Fixes: +* Provide a clear error when users wrongly combine `no_args` with + additional arguments (e.g. `expect().to receive().with(no_args, 1)`). + (Myron Marston, #786) +* Provide a clear error when users wrongly use `any_args` multiple times in the + same argument list (e.g. `expect().to receive().with(any_args, 1, any_args)`. + (Myron Marston, #786) + +### 3.1.3 / 2014-10-08 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) + +Bug Fixes: + * Correct received messages count when used with `have_received` matcher. (Jon Rowe, #793) * Provide a clear error message when you use `allow_any_instance_of(...)` or `expect_any_instance_of(...)` with the `have_received` matcher (they are not intended to be used together and previously caused an odd internal - failure in rspec-mocks). (Jon Rowe, #799) + failure in rspec-mocks). (Jon Rowe, #799). * Fix verified double `with` verification so that it applies to method stubs. (Myron Marston, #790) -* Provide a clear error when users wrongly combine `no_args` with - additional arguments (e.g. `expect().to receive().with(no_args, 1)`). - (Myron Marston, #786) -* Provide a clear error when users wrongly use `any_args` multiple times in the - same argument list (e.g. `expect().to receive().with(any_args, 1, any_args)`. - (Myron Marston, #786) ### 3.1.2 / 2014-09-26 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...v3.1.2) From c55d11bdca64161bf61528cb5627db32ad749666 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 Sep 2014 09:04:06 +0100 Subject: [PATCH 0742/1179] Use is_a_matcher? in arg_has_valid_description This fixes #685. It means that we only print descriptions in errors for matcher objects and not for user objects. --- lib/rspec/mocks/error_generator.rb | 4 +- spec/rspec/mocks/error_generator_spec.rb | 53 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 spec/rspec/mocks/error_generator_spec.rb diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index b9260f33e..25bf33ad5 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -226,9 +226,7 @@ def arg_list(*args) end def arg_has_valid_description(arg) - return false unless arg.respond_to?(:description) - - !arg.description.nil? && !arg.description.empty? + RSpec::Support.is_a_matcher?(arg) && arg.respond_to?(:description) end def format_received_args(*args) diff --git a/spec/rspec/mocks/error_generator_spec.rb b/spec/rspec/mocks/error_generator_spec.rb new file mode 100644 index 000000000..3eaa746cf --- /dev/null +++ b/spec/rspec/mocks/error_generator_spec.rb @@ -0,0 +1,53 @@ +require "spec_helper" + +module RSpec + module Mocks + RSpec.describe ErrorGenerator do + def unexpected_failure_message_for(object_description) + /received unexpected message :bees with \(#{object_description}\)/ + end + + describe "formatting arguments" do + context "on non-matcher objects that define #description" do + it "does not use the object's description" do + o = double(:double, :description => "Friends") + expect { + o.bees(o) + }.to fail_with(unexpected_failure_message_for(o.inspect)) + end + end + + context "on matcher objects" do + matcher :fake_matcher do + match { false } + end + + context "that define description" do + it "uses the object's description" do + d = double(:double) + o = fake_matcher + expect { + d.bees(o) + }.to raise_error(unexpected_failure_message_for(o.description)) + end + end + + context "that do not define description" do + it "does not use the object's description" do + d = double(:double) + o = Class.new do + def self.name + "RSpec::Mocks::ArgumentMatchers::" + end + end.new + + expect { + d.bees(o) + }.to fail_with(unexpected_failure_message_for(o.inspect)) + end + end + end + end + end + end +end From 139473dd16b2309266134a41ad79fbf8f36310d3 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 3 Aug 2014 11:40:25 +0100 Subject: [PATCH 0743/1179] Implement diffing between args for mock expectations. --- lib/rspec/mocks/configuration.rb | 18 +++ lib/rspec/mocks/error_generator.rb | 58 ++++++++-- rspec-mocks.gemspec | 2 + spec/rspec/mocks/diffing_spec.rb | 141 +++++++++++++++++++++++ spec/rspec/mocks/double_spec.rb | 2 +- spec/rspec/mocks/error_generator_spec.rb | 2 + spec/spec_helper.rb | 2 + 7 files changed, 212 insertions(+), 13 deletions(-) create mode 100644 spec/rspec/mocks/diffing_spec.rb diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 6da1ce2e1..b9ee5a2bf 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -123,6 +123,24 @@ def verify_partial_doubles? @verify_partial_doubles end + if ::RSpec.respond_to?(:configuration) + def color? + ::RSpec.configuration.color_enabled? + end + else + # Indicates whether or not diffs should be colored. + # Delegates to rspec-core's color option if rspec-core + # is loaded; otherwise you can set it here. + attr_writer :color + + # Indicates whether or not diffs should be colored. + # Delegates to rspec-core's color option if rspec-core + # is loaded; otherwise you can set it here. + def color? + @color + end + end + # Monkey-patch `Marshal.dump` to enable dumping of mocked or stubbed # objects. By default this will not work since RSpec mocks works by # adding singleton methods that cannot be serialized. This patch removes diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 25bf33ad5..7f306a27f 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -40,28 +40,28 @@ def raise_unexpected_message_error(message, *args) def raise_unexpected_message_args_error(expectation, *args) expected_args = format_args(*expectation.expected_args) actual_args = format_received_args(*args) - __raise "#{intro} received #{expectation.message.inspect} with " \ - "unexpected arguments\n expected: #{expected_args}\n" \ - " got: #{actual_args}" + diff = diff_message(expectation.expected_args, args) + message = "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}" + message << "\nDiff:#{diff}" unless diff.empty? + __raise message end # @private def raise_missing_default_stub_error(expectation, *args) expected_args = format_args(*expectation.expected_args) actual_args = format_received_args(*args) - __raise "#{intro} received #{expectation.message.inspect} with " \ - "unexpected arguments\n expected: #{expected_args}\n" \ - " got: #{actual_args}\n Please stub a default value " \ - "first if message might be received with other args as well. \n" + diff = diff_message(expectation.expected_args, args) + message = "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}" + message << "Diff:\n #{diff}" unless diff.empty? + message << "\n Please stub a default value first if message might be received with other args as well. \n" + __raise message end # @private def raise_similar_message_args_error(expectation, *args_for_multiple_calls) expected_args = format_args(*expectation.expected_args) actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ") - __raise "#{intro} received #{expectation.message.inspect} with " \ - "unexpected arguments\n expected: #{expected_args}\n" \ - " got: #{actual_args}" + __raise "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}" end # rubocop:disable Style/ParameterLists @@ -194,6 +194,40 @@ def self.raise_double_negation_error(wrapped_expression) private + def unexpected_arguments_message(expected_args_string, actual_args_string) + "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}" + end + + def diff_message(expected_args, actual_args) + formatted_expected_args = expected_args.map do |x| + if arg_has_valid_description?(x) + x.description + else + x + end + end + + formatted_expected_args, actual_args = unpack_string_args(formatted_expected_args, actual_args) + + differ.diff(actual_args, formatted_expected_args) + end + + def unpack_string_args(formatted_expected_args, actual_args) + if [formatted_expected_args, actual_args].all? { |x| list_of_exactly_one_string?(x) } + [formatted_expected_args.first, actual_args.first] + else + [formatted_expected_args, actual_args] + end + end + + def list_of_exactly_one_string?(args) + Array === args && args.count == 1 && String === args.first + end + + def differ + RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?) + end + def intro if @name "Double #{@name.inspect}" @@ -222,10 +256,10 @@ def format_args(*args) end def arg_list(*args) - args.map { |arg| arg_has_valid_description(arg) ? arg.description : arg.inspect }.join(", ") + args.map { |arg| arg_has_valid_description?(arg) ? arg.description : arg.inspect }.join(", ") end - def arg_has_valid_description(arg) + def arg_has_valid_description?(arg) RSpec::Support.is_a_matcher?(arg) && arg.respond_to?(:description) end diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 5a2b500a3..237b09010 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -37,6 +37,8 @@ Gem::Specification.new do |s| s.add_runtime_dependency "rspec-support", "~> #{RSpec::Mocks::Version::STRING.split('.')[0..1].concat(['0']).join('.')}" end + s.add_runtime_dependency "diff-lcs", ">= 1.2.0", "< 2.0" + s.add_development_dependency 'rake', '~> 10.0.0' s.add_development_dependency 'cucumber', '~> 1.3.15' s.add_development_dependency 'aruba', '~> 0.5' diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb new file mode 100644 index 000000000..8ddb5fc6e --- /dev/null +++ b/spec/rspec/mocks/diffing_spec.rb @@ -0,0 +1,141 @@ +require "spec_helper" +require "pp" + +RSpec.describe "Diffs printed when arguments don't match" do + before do + allow(RSpec::Mocks.configuration).to receive(:color?).and_return(false) + end + + def with_unfulfilled_double + d = double("double") + yield d + reset d + end + + context "with a non matcher object" do + it "does not print a diff when single line arguments are mismatched" do + with_unfulfilled_double do |d| + expect(d).to receive(:foo).with("some string") + expect { + d.foo("this other string") + }.to fail_with(a_string_excluding("Diff:")) + end + end + + it "prints a diff of the strings for individual mismatched multi-line string arguments" do + with_unfulfilled_double do |d| + expect(d).to receive(:foo).with("some string\nline2") + expect { + d.foo("this other string") + }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (\"some string\\nline2\")\n got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\n-line2\n+this other string\n") + end + end + + it "prints a diff of the args lists for multiple mismatched string arguments" do + with_unfulfilled_double do |d| + expect(d).to receive(:foo).with("some string\nline2", "some other string") + expect { + d.foo("this other string") + }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (\"some string\\nline2\", \"some other string\")\n got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\\nline2\n-some other string\n+this other string\n") + end + end + + it "does not print a diff when multiple single-line string arguments are mismatched" do + with_unfulfilled_double do |d| + expect(d).to receive(:foo).with("some string", "some other string") + expect { + d.foo("this other string", "a fourth string") + }.to fail_with(a_string_excluding("Diff:")) + end + end + + let(:expected_hash) { {:foo => :bar, :baz => :quz} } + + let(:actual_hash) { {:bad => :hash} } + + it "prints a diff with hash args" do + with_unfulfilled_double do |d| + expect(d).to receive(:foo).with(expected_hash) + expect { + d.foo(:bad => :hash) + }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (#{expected_hash.inspect})\n got: (#{actual_hash.inspect})\nDiff:\n@@ -1,2 +1,2 @@\n-[#{expected_hash.inspect}]\n+[#{actual_hash.inspect}]\n") + end + end + + it "prints a diff with an expected hash arg and a non-hash actual arg" do + with_unfulfilled_double do |d| + expect(d).to receive(:foo).with(expected_hash) + expect { + d.foo(Object.new) + }.to fail_with(/-\[#{Regexp.escape(expected_hash.inspect)}\].*\+\[#\]/m) + end + end + + it "prints a diff with array args" do + with_unfulfilled_double do |d| + expect(d).to receive(:foo).with([:a, :b, :c]) + expect { + d.foo([]) + }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: ([:a, :b, :c])\n got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[[:a, :b, :c]]\n+[[]]\n") + end + end + + context "that defines #description" do + it "does not use the object's description for a non-matcher object that implements #description" do + with_unfulfilled_double do |d| + + collab = double(:collab, :description => "This string") + collab_inspect = collab.inspect + + expect(d).to receive(:foo).with(collab) + expect { + d.foo([]) + }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (#{collab_inspect})\n got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_inspect}]\n+[[]]\n") + end + end + end + end + + context "with a matcher object" do + matcher :fake_matcher do + match { false } + end + + context "that defines #description" do + it "uses the object's description" do + with_unfulfilled_double do |d| + + collab = fake_matcher + collab_description = collab.description + + expect(d).to receive(:foo).with(collab) + expect { + d.foo([:a, :b]) + }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (#{collab_description})\n got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[\"#{collab_description}\"]\n+[[:a, :b]]\n") + end + end + end + + context "that does not define #description" do + it "for a matcher object that does not implement #description" do + with_unfulfilled_double do |d| + collab = Class.new do + def self.name + "RSpec::Mocks::ArgumentMatchers::" + end + end.new + + expect(RSpec::Support.is_a_matcher?(collab)).to be true + + collab_inspect = collab.inspect + collab_pp = PP.pp(collab, "").strip + + expect(d).to receive(:foo).with(collab) + expect { + d.foo([:a, :b]) + }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (#{collab_inspect})\n got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_pp}]\n+[[:a, :b]]\n") + end + end + end + end +end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 1f35d6c5c..cb6bb33bb 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -257,7 +257,7 @@ def @double.method_with_default_argument(arg={}); end expect { @double.method_with_default_argument(nil) verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)") + }.to raise_error(RSpec::Mocks::MockExpectationError, a_string_starting_with("Double \"test double\" received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)")) end end diff --git a/spec/rspec/mocks/error_generator_spec.rb b/spec/rspec/mocks/error_generator_spec.rb index 3eaa746cf..e196c7d7e 100644 --- a/spec/rspec/mocks/error_generator_spec.rb +++ b/spec/rspec/mocks/error_generator_spec.rb @@ -41,6 +41,8 @@ def self.name end end.new + expect(RSpec::Support.is_a_matcher?(o)).to be true + expect { d.bees(o) }.to fail_with(unexpected_failure_message_for(o.inspect)) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 58b15bdb2..32ebb09a6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -79,6 +79,8 @@ def prevents(msg = //, &block) config.include VerificationHelpers config.extend RSpec::Support::RubyFeatures config.include RSpec::Support::RubyFeatures + + RSpec::Matchers.define_negated_matcher :a_string_excluding, :include end RSpec.shared_context "with syntax" do |syntax| From 29ee51dc8fa1276d9c844dd4782d19f141378f20 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 16 Sep 2014 15:05:08 +0100 Subject: [PATCH 0744/1179] Add a changelog entry for #751 --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 898a576c7..87dd406b0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Enhancements: * Treat `any_args` as an arg splat, allowing it to match an arbitrary number of args at any point in an arg list. (Myron Marston, #786) +* Print diffs when arguments in mock expectations are mismatched. + (Sam Phippen, #751) Bug Fixes: @@ -14,6 +16,9 @@ Bug Fixes: * Provide a clear error when users wrongly use `any_args` multiple times in the same argument list (e.g. `expect().to receive().with(any_args, 1, any_args)`. (Myron Marston, #786) +* Prevent the error generator from using user object #description methods. + See [#685](https://github.com/rspec/rspec-mocks/issues/685). + (Sam Phippen, #751) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) From cbc739adcbd98f1a6e5e9e53353d285ee4df389a Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 21 Oct 2014 12:45:03 +0100 Subject: [PATCH 0745/1179] Trim line length in error generator --- lib/rspec/mocks/error_generator.rb | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 7f306a27f..cd498fa4a 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -41,8 +41,10 @@ def raise_unexpected_message_args_error(expectation, *args) expected_args = format_args(*expectation.expected_args) actual_args = format_received_args(*args) diff = diff_message(expectation.expected_args, args) - message = "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}" + + message = default_error_message(expectation, expected_args, actual_args) message << "\nDiff:#{diff}" unless diff.empty? + __raise message end @@ -51,9 +53,11 @@ def raise_missing_default_stub_error(expectation, *args) expected_args = format_args(*expectation.expected_args) actual_args = format_received_args(*args) diff = diff_message(expectation.expected_args, args) - message = "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}" + + message = default_error_message(expectation, expected_args, actual_args) message << "Diff:\n #{diff}" unless diff.empty? message << "\n Please stub a default value first if message might be received with other args as well. \n" + __raise message end @@ -61,7 +65,17 @@ def raise_missing_default_stub_error(expectation, *args) def raise_similar_message_args_error(expectation, *args_for_multiple_calls) expected_args = format_args(*expectation.expected_args) actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ") - __raise "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}" + + __raise(default_error_message(expectation, expected_args, actual_args)) + end + + def default_error_message(expectation, expected_args, actual_args) + [ + intro, + "received", + expectation.message.inspect, + unexpected_arguments_message(expected_args, actual_args), + ].join(" ") end # rubocop:disable Style/ParameterLists From c18a94397a7059e2c9df131b5280aacbc0e10542 Mon Sep 17 00:00:00 2001 From: Andy Waite Date: Fri, 24 Oct 2014 17:16:51 +0100 Subject: [PATCH 0746/1179] Fix minor typo (veryifing -> verifying) --- features/verifying_doubles/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifying_doubles/README.md b/features/verifying_doubles/README.md index 691c75fdd..d27acacb7 100644 --- a/features/verifying_doubles/README.md +++ b/features/verifying_doubles/README.md @@ -1,7 +1,7 @@ Verifying doubles are a stricter alternative to [normal doubles](./basics/test-doubles) that provide guarantees about what is being verified. When using verifying doubles, RSpec will check that the methods being stubbed are actually present on the underlying object if it is available. Prefer using -veryifing doubles over normal doubles. +verifying doubles over normal doubles. No checking will happen if the underlying object or class is not defined, but when run with it present (either as a full spec run or by explicitly preloading collaborators) a failure will be From 6ff6bebf54b5a39e1354981a6bc9850cd632c9d2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 27 Oct 2014 14:31:27 -0700 Subject: [PATCH 0747/1179] Split public from private interface of MessageExpectation. These things were combined all together, which made it more difficult to tell which was which. Also, `MessageExpectation` was labeled `@private`, which prevented these methods from showing up on rubydoc.info. --- lib/rspec/mocks/message_expectation.rb | 441 ++++++++++++------------- 1 file changed, 214 insertions(+), 227 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index c34a3cbba..4c0ca5ea0 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -34,48 +34,10 @@ def verify_messages_received end end - # @private + # Represents and individual method stub or message expectation. The methods + # defined here can be used to configure how it behaves. Each method returns + # `self` so that they can be chained together to form a fluent interface. class MessageExpectation - # @private - attr_accessor :error_generator, :implementation - attr_reader :message - attr_reader :orig_object - attr_writer :expected_received_count, :expected_from, :argument_list_matcher - protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation= - - # rubocop:disable Style/ParameterLists - # @private - def initialize(error_generator, expectation_ordering, expected_from, method_double, - type=:expectation, opts={}, &implementation_block) - @error_generator = error_generator - @error_generator.opts = opts - @expected_from = expected_from - @method_double = method_double - @orig_object = @method_double.object - @message = @method_double.method_name - @actual_received_count = 0 - @expected_received_count = type == :expectation ? 1 : :any - @argument_list_matcher = ArgumentListMatcher::MATCH_ALL - @order_group = expectation_ordering - @order_group.register(self) unless type == :stub - @expectation_type = type - @ordered = false - @at_least = @at_most = @exactly = nil - @args_to_yield = [] - @failed_fast = nil - @eval_context = nil - @yield_receiver_to_implementation_block = false - - @implementation = Implementation.new - self.inner_implementation_action = implementation_block - end - # rubocop:enable Style/ParameterLists - - # @private - def expected_args - @argument_list_matcher.expected_args - end - # @overload and_return(value) # @overload and_return(first_value, second_value) # @@ -116,15 +78,6 @@ def and_return(first_value, *values) nil end - def and_yield_receiver_to_implementation - @yield_receiver_to_implementation_block = true - self - end - - def yield_receiver_to_implementation_block? - @yield_receiver_to_implementation_block - end - # Tells the object to delegate to the original unmodified method # when it receives the message. # @@ -223,115 +176,6 @@ def and_yield(*args, &block) self end - # @private - def matches?(message, *args) - @message == message && @argument_list_matcher.args_match?(*args) - end - - # @private - def safe_invoke(parent_stub, *args, &block) - invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block) - end - - # @private - def invoke(parent_stub, *args, &block) - invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block) - end - - # @private - def invoke_without_incrementing_received_count(parent_stub, *args, &block) - invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block) - end - - # @private - def negative? - @expected_received_count == 0 && !@at_least - end - - # @private - def called_max_times? - @expected_received_count != :any && - !@at_least && - @expected_received_count > 0 && - @actual_received_count >= @expected_received_count - end - - # @private - def matches_name_but_not_args(message, *args) - @message == message && !@argument_list_matcher.args_match?(*args) - end - - # @private - def verify_messages_received - InsertOntoBacktrace.line(@expected_from) do - generate_error unless expected_messages_received? || failed_fast? - end - end - - # @private - def expected_messages_received? - ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count? - end - - def ensure_expected_ordering_received! - @order_group.verify_invocation_order(self) if @ordered - true - end - - # @private - def ignoring_args? - @expected_received_count == :any - end - - # @private - def matches_at_least_count? - @at_least && @actual_received_count >= @expected_received_count - end - - # @private - def matches_at_most_count? - @at_most && @actual_received_count <= @expected_received_count - end - - # @private - def matches_exact_count? - @expected_received_count == @actual_received_count - end - - # @private - def similar_messages - @similar_messages ||= [] - end - - # @private - def advise(*args) - similar_messages << args - end - - # @private - def generate_error - if similar_messages.empty? - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args) - else - @error_generator.raise_similar_message_args_error(self, *@similar_messages) - end - end - - def expectation_count_type - return :at_least if @at_least - return :at_most if @at_most - nil - end - - # @private - def description - @error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args) - end - - def raise_out_of_order_error - @error_generator.raise_out_of_order_error @message - end - # Constrains a stub or message expectation to invocations with specific # arguments. # @@ -482,93 +326,236 @@ def ordered(&block) end # @private - def additional_expected_calls - return 0 if @expectation_type == :stub || !@exactly - @expected_received_count - 1 - end + # Contains the parts of `MessageExpecation` that aren't part of + # rspec-mocks' public API. The class is very big and could really use + # some collaborators it delegates to for this stuff but for now this was + # the simplest way to split the public from private stuff to make it + # easier to publish the docs for the APIs we want published. + module ImplementationDetails + attr_accessor :error_generator, :implementation + attr_reader :message + attr_reader :orig_object + attr_writer :expected_received_count, :expected_from, :argument_list_matcher + protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation= + + # rubocop:disable Style/ParameterLists + def initialize(error_generator, expectation_ordering, expected_from, method_double, + type=:expectation, opts={}, &implementation_block) + @error_generator = error_generator + @error_generator.opts = opts + @expected_from = expected_from + @method_double = method_double + @orig_object = @method_double.object + @message = @method_double.method_name + @actual_received_count = 0 + @expected_received_count = type == :expectation ? 1 : :any + @argument_list_matcher = ArgumentListMatcher::MATCH_ALL + @order_group = expectation_ordering + @order_group.register(self) unless type == :stub + @expectation_type = type + @ordered = false + @at_least = @at_most = @exactly = nil + @args_to_yield = [] + @failed_fast = nil + @eval_context = nil + @yield_receiver_to_implementation_block = false - # @private - def ordered? - @ordered - end + @implementation = Implementation.new + self.inner_implementation_action = implementation_block + end + # rubocop:enable Style/ParameterLists - # @private - def negative_expectation_for?(message) - @message == message && negative? - end + def expected_args + @argument_list_matcher.expected_args + end - # @private - def actual_received_count_matters? - @at_least || @at_most || @exactly - end + def and_yield_receiver_to_implementation + @yield_receiver_to_implementation_block = true + self + end - # @private - def increase_actual_received_count! - @actual_received_count += 1 - end + def yield_receiver_to_implementation_block? + @yield_receiver_to_implementation_block + end - private + def matches?(message, *args) + @message == message && @argument_list_matcher.args_match?(*args) + end + + def safe_invoke(parent_stub, *args, &block) + invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block) + end + + def invoke(parent_stub, *args, &block) + invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block) + end + + def invoke_without_incrementing_received_count(parent_stub, *args, &block) + invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block) + end - def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) - args.unshift(orig_object) if yield_receiver_to_implementation_block? + def negative? + @expected_received_count == 0 && !@at_least + end - if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count)) - @actual_received_count += increment - @failed_fast = true - # args are the args we actually received, @argument_list_matcher is the - # list of args we were expecting - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args) + def called_max_times? + @expected_received_count != :any && + !@at_least && + @expected_received_count > 0 && + @actual_received_count >= @expected_received_count end - @order_group.handle_order_constraint self + def matches_name_but_not_args(message, *args) + @message == message && !@argument_list_matcher.args_match?(*args) + end - begin - if implementation.present? - implementation.call(*args, &block) - elsif parent_stub - parent_stub.invoke(nil, *args, &block) + def verify_messages_received + InsertOntoBacktrace.line(@expected_from) do + generate_error unless expected_messages_received? || failed_fast? end - ensure - @actual_received_count += increment end - end - def failed_fast? - @failed_fast - end + def expected_messages_received? + ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count? + end - def set_expected_received_count(relativity, n) - @at_least = (relativity == :at_least) - @at_most = (relativity == :at_most) - @exactly = (relativity == :exactly) - @expected_received_count = case n - when Numeric then n - when :once then 1 - when :twice then 2 - when :thrice then 3 - end - end + def ensure_expected_ordering_received! + @order_group.verify_invocation_order(self) if @ordered + true + end - def initial_implementation_action=(action) - implementation.initial_action = action - end + def ignoring_args? + @expected_received_count == :any + end - def inner_implementation_action=(action) - return unless action - warn_about_stub_override if implementation.inner_action - implementation.inner_action = action - end + def matches_at_least_count? + @at_least && @actual_received_count >= @expected_received_count + end - def terminal_implementation_action=(action) - implementation.terminal_action = action - end + def matches_at_most_count? + @at_most && @actual_received_count <= @expected_received_count + end + + def matches_exact_count? + @expected_received_count == @actual_received_count + end - def warn_about_stub_override - RSpec.warning( - "You're overriding a previous stub implementation of `#{@message}`. " \ - "Called from #{CallerFilter.first_non_rspec_line}." - ) + def similar_messages + @similar_messages ||= [] + end + + def advise(*args) + similar_messages << args + end + + def generate_error + if similar_messages.empty? + @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args) + else + @error_generator.raise_similar_message_args_error(self, *@similar_messages) + end + end + + def expectation_count_type + return :at_least if @at_least + return :at_most if @at_most + nil + end + + def description + @error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args) + end + + def raise_out_of_order_error + @error_generator.raise_out_of_order_error @message + end + + def additional_expected_calls + return 0 if @expectation_type == :stub || !@exactly + @expected_received_count - 1 + end + + def ordered? + @ordered + end + + def negative_expectation_for?(message) + @message == message && negative? + end + + def actual_received_count_matters? + @at_least || @at_most || @exactly + end + + def increase_actual_received_count! + @actual_received_count += 1 + end + + private + + def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) + args.unshift(orig_object) if yield_receiver_to_implementation_block? + + if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count)) + @actual_received_count += increment + @failed_fast = true + # args are the args we actually received, @argument_list_matcher is the + # list of args we were expecting + @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args) + end + + @order_group.handle_order_constraint self + + begin + if implementation.present? + implementation.call(*args, &block) + elsif parent_stub + parent_stub.invoke(nil, *args, &block) + end + ensure + @actual_received_count += increment + end + end + + def failed_fast? + @failed_fast + end + + def set_expected_received_count(relativity, n) + @at_least = (relativity == :at_least) + @at_most = (relativity == :at_most) + @exactly = (relativity == :exactly) + @expected_received_count = case n + when Numeric then n + when :once then 1 + when :twice then 2 + when :thrice then 3 + end + end + + def initial_implementation_action=(action) + implementation.initial_action = action + end + + def inner_implementation_action=(action) + return unless action + warn_about_stub_override if implementation.inner_action + implementation.inner_action = action + end + + def terminal_implementation_action=(action) + implementation.terminal_action = action + end + + def warn_about_stub_override + RSpec.warning( + "You're overriding a previous stub implementation of `#{@message}`. " \ + "Called from #{CallerFilter.first_non_rspec_line}." + ) + end end + + include ImplementationDetails end # Handles the implementation of an `and_yield` declaration. From 5caf5bcaee6fbb32848c29f94f9c762cf5708cc5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 27 Oct 2014 14:44:17 -0700 Subject: [PATCH 0748/1179] Improve MessageExpectation docs. - Group the methods. - Specify return values. - Remove blank lines that were rendering in example output. --- lib/rspec/mocks/message_expectation.rb | 114 +++++++++++++------------ 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 4c0ca5ea0..915951f93 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -34,10 +34,12 @@ def verify_messages_received end end - # Represents and individual method stub or message expectation. The methods - # defined here can be used to configure how it behaves. Each method returns + # Represents an individual method stub or message expectation. The methods + # defined here can be used to configure how it behaves. The methods return # `self` so that they can be chained together to form a fluent interface. class MessageExpectation + # @!group Configuring Responses + # @overload and_return(value) # @overload and_return(first_value, second_value) # @@ -49,8 +51,8 @@ class MessageExpectation # If the message is received more times than there are values, the last # value is received for every subsequent call. # + # @return [nil] No further chaining is supported after this. # @example - # # allow(counter).to receive(:count).and_return(1) # counter.count # => 1 # counter.count # => 1 @@ -83,8 +85,8 @@ def and_return(first_value, *values) # # @note This is only available on partial doubles. # + # @return [nil] No further chaining is supported after this. # @example - # # expect(counter).to receive(:increment).and_call_original # original_count = counter.count # counter.increment @@ -102,12 +104,11 @@ def and_call_original # # @note This is only available on partial doubles. # + # @return [nil] No further chaining is supported after this. # @example - # # expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block| # original_method.call(*args, &block).first(10) # end - # def and_wrap_original(&block) if RSpec::Mocks::TestDouble === @method_double.object @error_generator.raise_only_valid_on_a_partial_double(:and_call_original) @@ -116,6 +117,8 @@ def and_wrap_original(&block) @implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block) @yield_receiver_to_implementation_block = false end + + nil end # @overload and_raise @@ -125,8 +128,8 @@ def and_wrap_original(&block) # # Tells the object to raise an exception when the message is received. # + # @return [nil] No further chaining is supported after this. # @note - # # When you pass an exception class, the MessageExpectation will raise # an instance of it, creating it with `exception` and passing `message` # if specified. If the exception class initializer requires more than @@ -134,7 +137,6 @@ def and_wrap_original(&block) # otherwise this method will raise an ArgumentError exception. # # @example - # # allow(car).to receive(:go).and_raise # allow(car).to receive(:go).and_raise(OutOfGas) # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive") @@ -154,8 +156,8 @@ def and_raise(exception=RuntimeError, message=nil) # Tells the object to throw a symbol (with the object if that form is # used) when the message is received. # + # @return [nil] No further chaining is supported after this. # @example - # # allow(car).to receive(:go).and_throw(:out_of_gas) # allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1) def and_throw(*args) @@ -166,8 +168,8 @@ def and_throw(*args) # Tells the object to yield one or more args to a block when the message # is received. # + # @return [MessageExpecation] self, to support further chaining. # @example - # # stream.stub(:open).and_yield(StringIO.new) def and_yield(*args, &block) yield @eval_context = Object.new if block @@ -175,47 +177,15 @@ def and_yield(*args, &block) self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator) self end + # @!endgroup - # Constrains a stub or message expectation to invocations with specific - # arguments. - # - # With a stub, if the message might be received with other args as well, - # you should stub a default value first, and then stub or mock the same - # message using `with` to constrain to specific arguments. - # - # A message expectation will fail if the message is received with different - # arguments. - # - # @example - # - # allow(cart).to receive(:add) { :failure } - # allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } - # cart.add(Book.new(:isbn => 1234567890)) - # # => :failure - # cart.add(Book.new(:isbn => 1934356379)) - # # => :success - # - # expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } - # cart.add(Book.new(:isbn => 1234567890)) - # # => failed expectation - # cart.add(Book.new(:isbn => 1934356379)) - # # => passes - def with(*args, &block) - if args.empty? - raise ArgumentError, - "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments." - end - - self.inner_implementation_action = block - @argument_list_matcher = ArgumentListMatcher.new(*args) - self - end + # @!group Constraining Receive Counts # Constrain a message expectation to be received a specific number of # times. # + # @return [MessageExpecation] self, to support further chaining. # @example - # # expect(dealer).to receive(:deal_card).exactly(10).times def exactly(n, &block) self.inner_implementation_action = block @@ -226,8 +196,8 @@ def exactly(n, &block) # Constrain a message expectation to be received at least a specific # number of times. # + # @return [MessageExpecation] self, to support further chaining. # @example - # # expect(dealer).to receive(:deal_card).at_least(9).times def at_least(n, &block) set_expected_received_count :at_least, n @@ -244,8 +214,8 @@ def at_least(n, &block) # Constrain a message expectation to be received at most a specific # number of times. # + # @return [MessageExpecation] self, to support further chaining. # @example - # # expect(dealer).to receive(:deal_card).at_most(10).times def at_most(n, &block) self.inner_implementation_action = block @@ -255,8 +225,8 @@ def at_most(n, &block) # Syntactic sugar for `exactly`, `at_least` and `at_most` # + # @return [MessageExpecation] self, to support further chaining. # @example - # # expect(dealer).to receive(:deal_card).exactly(10).times # expect(dealer).to receive(:deal_card).at_least(10).times # expect(dealer).to receive(:deal_card).at_most(10).times @@ -267,8 +237,8 @@ def times(&block) # Expect a message not to be received at all. # + # @return [MessageExpecation] self, to support further chaining. # @example - # # expect(car).to receive(:stop).never def never ErrorGenerator.raise_double_negation_error("expect(obj)") if negative? @@ -278,8 +248,8 @@ def never # Expect a message to be received exactly one time. # + # @return [MessageExpecation] self, to support further chaining. # @example - # # expect(car).to receive(:go).once def once(&block) self.inner_implementation_action = block @@ -289,8 +259,8 @@ def once(&block) # Expect a message to be received exactly two times. # + # @return [MessageExpecation] self, to support further chaining. # @example - # # expect(car).to receive(:go).twice def twice(&block) self.inner_implementation_action = block @@ -300,19 +270,57 @@ def twice(&block) # Expect a message to be received exactly three times. # + # @return [MessageExpecation] self, to support further chaining. # @example - # # expect(car).to receive(:go).thrice def thrice(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 3 self end + # @!endgroup - # Expect messages to be received in a specific order. + # @!group Other Constraints + + # Constrains a stub or message expectation to invocations with specific + # arguments. + # + # With a stub, if the message might be received with other args as well, + # you should stub a default value first, and then stub or mock the same + # message using `with` to constrain to specific arguments. # + # A message expectation will fail if the message is received with different + # arguments. + # + # @return [MessageExpecation] self, to support further chaining. # @example + # allow(cart).to receive(:add) { :failure } + # allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } + # cart.add(Book.new(:isbn => 1234567890)) + # # => :failure + # cart.add(Book.new(:isbn => 1934356379)) + # # => :success + # + # expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } + # cart.add(Book.new(:isbn => 1234567890)) + # # => failed expectation + # cart.add(Book.new(:isbn => 1934356379)) + # # => passes + def with(*args, &block) + if args.empty? + raise ArgumentError, + "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments." + end + + self.inner_implementation_action = block + @argument_list_matcher = ArgumentListMatcher.new(*args) + self + end + + # Expect messages to be received in a specific order. # + # @return [MessageExpecation] self, to support further chaining. + # @example # expect(api).to receive(:prepare).ordered # expect(api).to receive(:run).ordered # expect(api).to receive(:finish).ordered From 77c156ef36dc1b76cdc4f066eb48b98e18ecbc00 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 30 Oct 2014 08:23:40 -0700 Subject: [PATCH 0749/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 +- script/clone_all_rspec_repos | 4 +- script/functions.sh | 104 ++++++++-------------------------- script/predicate_functions.sh | 48 ++++++++++++++++ script/run_build | 12 ++-- script/travis_functions.sh | 66 +++++++++++++++++++++ 7 files changed, 150 insertions(+), 89 deletions(-) create mode 100644 script/predicate_functions.sh create mode 100644 script/travis_functions.sh diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 8de3db276..55fee1927 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 67cc66393..2367ca1b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -19,6 +19,7 @@ rvm: - 2.1.1 - 2.1.2 - 2.1.3 + - 2.1.4 - ruby-head - ree - jruby-18mode diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index d03e9f6f3..fb840d7ab 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,8 +1,8 @@ #!/bin/bash -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -set -e -x +set -e source script/functions.sh if is_mri; then diff --git a/script/functions.sh b/script/functions.sh index daf776e19..0ff87a8ba 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,80 +1,14 @@ -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. +source script/travis_functions.sh +source script/predicate_functions.sh + # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS="${JRUBY_OPTS} -X-C" # disable JIT since these processes are so short lived SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` -# Taken from: -# https://github.com/travis-ci/travis-build/blob/e9314616e182a23e6a280199cd9070bfc7cae548/lib/travis/build/script/templates/header.sh#L34-L53 -travis_retry() { - local result=0 - local count=1 - while [ $count -le 3 ]; do - [ $result -ne 0 ] && { - echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 - } - "$@" - result=$? - [ $result -eq 0 ] && break - count=$(($count + 1)) - sleep 1 - done - - [ $count -eq 3 ] && { - echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 - } - - return $result -} - -function is_mri { - if ruby -e "exit(!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby')"; then - # RUBY_ENGINE only returns 'ruby' on MRI. - # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) - return 0 - else - return 1 - fi; -} - -function is_mri_192 { - if is_mri; then - if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then - return 0 - else - return 1 - fi - else - return 1 - fi -} - -function rspec_support_compatible { - if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then - return 0 - else - return 1 - fi -} - -function documentation_enforced { - if [ -x ./bin/yard ]; then - return 0 - else - return 1 - fi -} - -function style_and_lint_enforced { - if [ -x ./bin/rubocop ]; then - return 0 - else - return 1 - fi -} - function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" @@ -90,6 +24,7 @@ function run_specs_and_record_done { rspec_bin=script/rspec_with_simplecov fi; + echo "${PWD}/bin/rspec" $rspec_bin spec --backtrace --format progress --profile --format progress --out $SPECS_HAVE_RUN_FILE } @@ -102,6 +37,8 @@ function run_cukes { # spec failures in our spec suite due to problems with this mode. export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' + echo "${PWD}/bin/cucumber" + if is_mri_192; then # For some reason we get SystemStackError on 1.9.2 when using # the bin/cucumber approach below. That approach is faster @@ -118,6 +55,8 @@ function run_cukes { } function run_specs_one_by_one { + echo "Running each spec file, one-by-one..." + for file in `find spec -iname '*_spec.rb'`; do bin/rspec $file -b --format progress done @@ -125,10 +64,8 @@ function run_specs_one_by_one { function run_spec_suite_for { if [ ! -f ../$1/$SPECS_HAVE_RUN_FILE ]; then # don't rerun specs that have already run - pushd ../$1 - echo echo "Running specs for $1" - echo + pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` travis_retry eval "bundle install $bundle_install_flags" @@ -138,8 +75,11 @@ function run_spec_suite_for { } function check_documentation_coverage { + echo "bin/yard stats --list-undoc" + bin/yard stats --list-undoc | ruby -e " while line = gets + has_warnings ||= line.start_with?('[warn]:') coverage ||= line[/([\d\.]+)% documented/, 1] puts line end @@ -148,21 +88,27 @@ function check_documentation_coverage { puts \"\n\nMissing documentation coverage (currently at #{coverage}%)\" exit(1) end + + if has_warnings + puts \"\n\nYARD emitted documentation warnings.\" + exit(1) + end " } function check_style_and_lint { + echo "bin/rubucop lib" bin/rubocop lib } function run_all_spec_suites { - run_specs_one_by_one - run_spec_suite_for "rspec-core" - run_spec_suite_for "rspec-expectations" - run_spec_suite_for "rspec-mocks" - run_spec_suite_for "rspec-rails" + fold "one-by-one specs" run_specs_one_by_one + fold "rspec-core specs" run_spec_suite_for "rspec-core" + fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" + fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" + fold "rspec-rails specs" run_spec_suite_for "rspec-rails" if rspec_support_compatible; then - run_spec_suite_for "rspec-support" + fold "rspec-support specs" run_spec_suite_for "rspec-support" fi } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh new file mode 100644 index 000000000..cc4a73dbb --- /dev/null +++ b/script/predicate_functions.sh @@ -0,0 +1,48 @@ +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +function is_mri { + if ruby -e "exit(!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby')"; then + # RUBY_ENGINE only returns 'ruby' on MRI. + # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) + return 0 + else + return 1 + fi; +} + +function is_mri_192 { + if is_mri; then + if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + +function rspec_support_compatible { + if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then + return 0 + else + return 1 + fi +} + +function documentation_enforced { + if [ -x ./bin/yard ]; then + return 0 + else + return 1 + fi +} + +function style_and_lint_enforced { + if [ -x ./bin/rubocop ]; then + return 0 + else + return 1 + fi +} diff --git a/script/run_build b/script/run_build index eb356b8bf..661821908 100755 --- a/script/run_build +++ b/script/run_build @@ -1,8 +1,8 @@ #!/bin/bash -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -set -e -x +set -e source script/functions.sh # Allow repos to override the default functions and add their own @@ -10,15 +10,15 @@ if [ -f script/custom_build_functions.sh ]; then source script/custom_build_functions.sh fi -run_specs_and_record_done -run_cukes +fold "specs" run_specs_and_record_done +fold "cukes" run_cukes if documentation_enforced; then - check_documentation_coverage + fold "doc check" check_documentation_coverage fi if style_and_lint_enforced; then - check_style_and_lint + fold "rubocop" check_style_and_lint fi if is_mri; then diff --git a/script/travis_functions.sh b/script/travis_functions.sh new file mode 100644 index 000000000..0e1715dba --- /dev/null +++ b/script/travis_functions.sh @@ -0,0 +1,66 @@ +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +# Taken from: +# https://github.com/travis-ci/travis-build/blob/e9314616e182a23e6a280199cd9070bfc7cae548/lib/travis/build/script/templates/header.sh#L34-L53 +travis_retry() { + local result=0 + local count=1 + while [ $count -le 3 ]; do + [ $result -ne 0 ] && { + echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 + } + "$@" + result=$? + [ $result -eq 0 ] && break + count=$(($count + 1)) + sleep 1 + done + + [ $count -eq 3 ] && { + echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 + } + + return $result +} + +# Taken from https://github.com/vcr/vcr/commit/fa96819c92b783ec0c794f788183e170e4f684b2 +# and https://github.com/vcr/vcr/commit/040aaac5370c68cd13c847c076749cd547a6f9b1 +nano_cmd="$(type -p gdate date | head -1)" +nano_format="+%s%N" +[ "$(uname -s)" != "Darwin" ] || nano_format="${nano_format/%N/000000000}" + +travis_time_start() { + travis_timer_id=$(printf %08x $(( RANDOM * RANDOM ))) + travis_start_time=$($nano_cmd -u "$nano_format") + printf "travis_time:start:%s\r\e[0m" $travis_timer_id +} + +travis_time_finish() { + local travis_end_time=$($nano_cmd -u "$nano_format") + local duration=$(($travis_end_time-$travis_start_time)) + printf "travis_time:end:%s:start=%s,finish=%s,duration=%s\r\e[0m" \ + $travis_timer_id $travis_start_time $travis_end_time $duration +} + +fold() { + local name="$1" + local status=0 + shift 1 + if [ -n "$TRAVIS" ]; then + printf "travis_fold:start:%s\r\e[0m" "$name" + travis_time_start + fi + + "$@" || status=$? + + [ -z "$TRAVIS" ] || travis_time_finish + + if [ "$status" -eq 0 ]; then + if [ -n "$TRAVIS" ]; then + printf "travis_fold:end:%s\r\e[0m" "$name" + fi + else + STATUS="$status" + fi +} From 6a7e9601a525c01322cdb881e1a9ff55544b8148 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 12 Nov 2014 00:37:08 -0800 Subject: [PATCH 0750/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 16 +++++++++++++++- script/predicate_functions.sh | 20 ++++++++++++++++++-- script/run_build | 2 +- script/travis_functions.sh | 4 +++- 7 files changed, 40 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 55fee1927..4008c0809 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 2367ca1b9..5702a058d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index fb840d7ab..74651bd6b 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 0ff87a8ba..903b45ed9 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. source script/travis_functions.sh @@ -94,6 +94,20 @@ function check_documentation_coverage { exit(1) end " + + # Some warnings only show up when generating docs, so do that as well. + bin/yard doc --no-cache | ruby -e " + while line = gets + has_warnings ||= line.start_with?('[warn]:') + has_errors ||= line.start_with?('[error]:') + puts line + end + + if has_warnings || has_errors + puts \"\n\nYARD emitted documentation warnings or errors.\" + exit(1) + end + " } function check_style_and_lint { diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index cc4a73dbb..19053f0be 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -23,6 +23,18 @@ function is_mri_192 { fi } +function is_mri_2plus { + if is_mri; then + if ruby -e "exit(RUBY_VERSION.to_f > 2.0)"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + function rspec_support_compatible { if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then return 0 @@ -33,7 +45,11 @@ function rspec_support_compatible { function documentation_enforced { if [ -x ./bin/yard ]; then - return 0 + if is_mri_2plus; then + return 0 + else + return 1 + fi else return 1 fi diff --git a/script/run_build b/script/run_build index 661821908..2075b66a2 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 0e1715dba..db6b014d6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: @@ -63,4 +63,6 @@ fold() { else STATUS="$status" fi + + return $status } From 58e23002af1457340125bcf7a5a2bcb4cd8a6cf9 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Wed, 12 Nov 2014 12:16:22 -0500 Subject: [PATCH 0751/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 7 ++++--- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4008c0809..0549c7069 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 5702a058d..fced5923d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 74651bd6b..431ecb34d 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 903b45ed9..b4111866a 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,8 +1,9 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -source script/travis_functions.sh -source script/predicate_functions.sh +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source $SCRIPT_DIR/travis_functions.sh +source $SCRIPT_DIR/predicate_functions.sh # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS="${JRUBY_OPTS} -X-C" # disable JIT since these processes are so short lived diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 19053f0be..14a4d8552 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 2075b66a2..c3f3f8dfd 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index db6b014d6..66cfd0230 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 2c97b39f5cfe48c12c8b4625764f36004830a982 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 15:30:21 -0800 Subject: [PATCH 0752/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 25 +++++++++++++++++++++++++ script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 appveyor.yml diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 0549c7069..34d4f8cd9 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index fced5923d..05cad069d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..495be4595 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,25 @@ +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +version: "{build}" + +# Disable normal Windows builds in favor of our test script. +build: off + +install: + - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% + - ruby --version + - gem --version + - gem install bundler + - bundler --version + - bundle install + - cinst ansicon + +test_script: + - bundle exec rspec + +environment: + matrix: + # ruby_version: '20' doesn't work for some reason + - ruby_version: '193' + - ruby_version: '21' diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 431ecb34d..e0ec88131 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index b4111866a..e5fb9eab4 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 14a4d8552..12e96326e 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index c3f3f8dfd..dbbfcb22e 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 66cfd0230..54c92a215 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:22-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 4521262cbd352b77a88c1028b48e2e5d71a5638a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 23:24:09 -0800 Subject: [PATCH 0753/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 10 +++++++++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 34d4f8cd9..6d6230caf 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 05cad069d..94f3ae6f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 495be4595..fb9bb1992 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,16 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" +# This will build all PRs targetting matching branches. +# Without this, each PR builds twice -- once for the PR branch HEAD, +# and once for the merge commit that github creates for each mergable PR. +branches: + only: + - master + - /.*-maintenance$/ + # Disable normal Windows builds in favor of our test script. build: off diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e0ec88131..2dea8c852 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index e5fb9eab4..235e92e79 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 12e96326e..019286f5a 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index dbbfcb22e..de108c1d2 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 54c92a215..f3f2f85b8 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 0879f1c713d9c261e9b7acd35c2eec6caad3aeec Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 15 Nov 2014 14:32:55 +1100 Subject: [PATCH 0754/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 ++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 6d6230caf..4c9839194 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 94f3ae6f0..307c1c339 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -20,6 +20,7 @@ rvm: - 2.1.2 - 2.1.3 - 2.1.4 + - 2.1.5 - ruby-head - ree - jruby-18mode diff --git a/appveyor.yml b/appveyor.yml index fb9bb1992..47d1d045b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 2dea8c852..131da0b0a 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 235e92e79..21778f9f7 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 019286f5a..5e6eb4ad0 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index de108c1d2..4f4764d0d 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index f3f2f85b8..82da2bc3e 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 9f0b16adfd4b3ca5a1afbe15b80402e1643c4ed6 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Thu, 20 Nov 2014 16:14:24 -0800 Subject: [PATCH 0755/1179] Remove spy scenario that is wrong level of detail for relish docs. Behaviour is already covered by specs. --- features/basics/spies.feature | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/features/basics/spies.feature b/features/basics/spies.feature index 275333f72..4f1b727e9 100644 --- a/features/basics/spies.feature +++ b/features/basics/spies.feature @@ -126,19 +126,3 @@ Feature: Spies | 2) An invitiation fails when an order constraint is not satisifed | | Failure/Error: expect(invitation).to have_received(:deliver).with("foo@example.com").ordered | | Double "invitation" received :deliver out of order | - - Scenario: `have_received` generates a good example description - Given a file named "generates_description_spec.rb" with: - """ruby - RSpec.describe "An invitation" do - subject(:invitation) { spy('invitation') } - before { invitation.deliver } - it { is_expected.to have_received(:deliver) } - end - """ - When I run `rspec --format documentation generates_description_spec.rb` - Then it should pass with: - """ - An invitation - should have received deliver(*(any args)) 1 time - """ From 288eea43b589bb82ead3db905069fa35e46f12f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 20 Nov 2014 22:11:34 -0800 Subject: [PATCH 0756/1179] Fix verified doubles when their const has already been stubbed. Fixes #823. --- Changelog.md | 4 ++ lib/rspec/mocks/object_reference.rb | 24 ++++++- .../class_double_with_class_loaded_spec.rb | 25 +++++++ ...class_double_with_class_not_loaded_spec.rb | 14 ++++ .../instance_double_with_class_loaded_spec.rb | 16 +++++ ...tance_double_with_class_not_loaded_spec.rb | 14 ++++ .../verifying_doubles/object_double_spec.rb | 66 +++++++++++++++++++ 7 files changed, 161 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 87dd406b0..6595e0c01 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,6 +19,10 @@ Bug Fixes: * Prevent the error generator from using user object #description methods. See [#685](https://github.com/rspec/rspec-mocks/issues/685). (Sam Phippen, #751) +* Make verified doubles declared as `(instance|class)_double(SomeConst)` + work properly when `SomeConst` has previously been stubbed. + `(instance|class)_double("SomeClass")` already worked properly. + (Myron Marston, #824) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index fce87d166..269e89ba5 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -6,8 +6,17 @@ class ObjectReference # on the given argument. def self.for(object_module_or_name, allow_direct_object_refs=false) case object_module_or_name - when Module then DirectModuleReference.new(object_module_or_name) - when String then NamedObjectReference.new(object_module_or_name) + when Module + if anonymous_module?(object_module_or_name) + DirectModuleReference.new(object_module_or_name) + else + # Use a `NamedObjectReference` if it has a name because this + # will use the original value of the constant in case it has + # been stubbed. + NamedObjectReference.new(object_module_or_name.name) + end + when String + NamedObjectReference.new(object_module_or_name) else if allow_direct_object_refs DirectObjectReference.new(object_module_or_name) @@ -17,6 +26,17 @@ def self.for(object_module_or_name, allow_direct_object_refs=false) end end end + + if Module.new.name.nil? + def self.anonymous_module?(mod) + !mod.name + end + else # 1.8.7 + def self.anonymous_module?(mod) + mod.name == "" + end + end + private_class_method :anonymous_module? end # Used when an object is passed to `object_double`. diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index b10a2dc55..331e657d2 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -113,6 +113,31 @@ module Mocks allow(dbl).to receive(:defined_class_method).with(2, :args) } end + + context "when given an anonymous class" do + it 'properly verifies' do + subclass = Class.new(LoadedClass) + o = class_double(subclass) + allow(o).to receive(:defined_class_method) + prevents { allow(o).to receive(:undefined_method) } + end + end + + context "when the class const has been previously stubbed" do + before { stub_const("LoadedClass", Class.new) } + + it "uses the original class to verify against for `class_double('ClassName')`" do + o = class_double("LoadedClass") + allow(o).to receive(:defined_class_method) + prevents { allow(o).to receive(:undefined_method) } + end + + it "uses the original class to verify against for `instance_double(ClassName)`" do + o = class_double(LoadedClass) + allow(o).to receive(:defined_class_method) + prevents { allow(o).to receive(:undefined_method) } + end + end end end end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb index b89b696d5..c551719d6 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb @@ -53,6 +53,20 @@ def use; end expect(double.use).to be(:ok) end end + + context "when the class const has been previously stubbed" do + before { stub_const("NonLoadedClass", Class.new) } + + it "treats the class as being unloaded for `class_double('NonLoadedClass')`" do + o = class_double("NonLoadedClass") + allow(o).to receive(:undefined_method) + end + + it "treats the class as being unloaded for `instance_double(NonLoadedClass)`" do + o = class_double(NonLoadedClass) + allow(o).to receive(:undefined_method) + end + end end end end diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index 5fb4342df..c9961e3c7 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -144,6 +144,22 @@ module Mocks expect(o.defined_instance_method).to eq(1) end + context "when the class const has been previously stubbed" do + before { class_double(LoadedClass).as_stubbed_const } + + it "uses the original class to verify against for `instance_double('LoadedClass')`" do + o = instance_double("LoadedClass") + allow(o).to receive(:defined_instance_method) + prevents { allow(o).to receive(:undefined_method) } + end + + it "uses the original class to verify against for `instance_double(LoadedClass)`" do + o = instance_double(LoadedClass) + allow(o).to receive(:defined_instance_method) + prevents { allow(o).to receive(:undefined_method) } + end + end + context 'for null objects' do let(:o) { instance_double('LoadedClass').as_null_object } diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb index e6f9822aa..a91978e19 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb @@ -52,6 +52,20 @@ module Mocks expect(o.respond_to?(:a, true)).to be true end end + + context "when the class const has been previously stubbed" do + before { class_double("NonLoadedClass").as_stubbed_const } + + it "treats the class as unloaded for `instance_double('NonLoadedClass')`" do + o = instance_double("NonLoadedClass") + allow(o).to receive(:undefined_method) + end + + it "treats the class as unloaded for `instance_double(NonLoadedClass)`" do + o = instance_double(NonLoadedClass) + allow(o).to receive(:undefined_method) + end + end end end end diff --git a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb index c26cb7b35..e32d844e8 100644 --- a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb @@ -63,6 +63,72 @@ module Mocks allow(dbl).to receive(:instance_method_with_two_args).with(3, :foo, :args) } end + + context "when a loaded object constant has previously been stubbed with an object" do + before { stub_const("LoadedClass::INSTANCE", Object.new) } + + it "uses the original object to verify against for `object_double('ConstName')`" do + o = object_double("LoadedClass::INSTANCE") + allow(o).to receive(:defined_instance_method) + prevents { allow(o).to receive(:undefined_meth) } + end + + it "uses the stubbed const value to verify against for `object_double(ConstName)`, " \ + "which probably isn't what the user wants, but there's nothing else we can do since " \ + "we can't get the constant name from the given object and thus cannot interrogate " \ + "our stubbed const registry to see it has been stubbed" do + o = object_double(LoadedClass::INSTANCE) + prevents { allow(o).to receive(:defined_instance_method) } + end + end + + context "when a loaded object constant has previously been stubbed with a class" do + before { stub_const("LoadedClass::INSTANCE", Class.new) } + + it "uses the original object to verify against for `object_double('ConstName')`" do + o = object_double("LoadedClass::INSTANCE") + allow(o).to receive(:defined_instance_method) + prevents { allow(o).to receive(:undefined_meth) } + end + + it "uses the original object to verify against for `object_double(ConstName)`" do + o = object_double(LoadedClass::INSTANCE) + allow(o).to receive(:defined_instance_method) + prevents { allow(o).to receive(:undefined_meth) } + end + end + + context "when an unloaded object constant has previously been stubbed with an object" do + before { stub_const("LoadedClass::NOINSTANCE", LoadedClass::INSTANCE) } + + it "treats it as being unloaded for `object_double('ConstName')`" do + o = object_double("LoadedClass::NOINSTANCE") + allow(o).to receive(:undefined_method) + end + + it "uses the stubbed const value to verify against for `object_double(ConstName)`, " \ + "which probably isn't what the user wants, but there's nothing else we can do since " \ + "we can't get the constant name from the given object and thus cannot interrogate " \ + "our stubbed const registry to see it has been stubbed" do + o = object_double(LoadedClass::NOINSTANCE) + allow(o).to receive(:defined_instance_method) + prevents { allow(o).to receive(:undefined_method) } + end + end + + context "when an unloaded object constant has previously been stubbed with a class" do + before { stub_const("LoadedClass::NOINSTANCE", Class.new) } + + it "treats it as being unloaded for `object_double('ConstName')`" do + o = object_double("LoadedClass::NOINSTANCE") + allow(o).to receive(:undefined_method) + end + + it "treats it as being unloaded for `object_double('ConstName')`" do + o = object_double(LoadedClass::NOINSTANCE) + allow(o).to receive(:undefined_method) + end + end end end end From 8dae3e89fbc413a9401b32b6f469ab6301f6c698 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 21 Nov 2014 08:24:00 -0800 Subject: [PATCH 0757/1179] Remove unnecessary captured block. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capturing blocks isn’t free, and `yield` (which we are already using) performs better than capturing the block and using `block.call`. --- lib/rspec/mocks/object_reference.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 269e89ba5..d62b71225 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -97,7 +97,7 @@ def const_to_replace end alias description const_to_replace - def when_loaded(&_block) + def when_loaded yield object if object end From ead0b5feabae467a65a19feb5f323f0773f00853 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 21 Nov 2014 08:25:05 -0800 Subject: [PATCH 0758/1179] Fix memoization to properly memoize nil. --- lib/rspec/mocks/object_reference.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index d62b71225..8b4035e4f 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -104,7 +104,8 @@ def when_loaded private def object - @object ||= Constant.original(@const_name).original_value + return @object if defined?(@object) + @object = Constant.original(@const_name).original_value end end end From cceb0e8d9a55751a91bc23766b8d96c9c2a68c5c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 25 Nov 2014 14:41:06 +1100 Subject: [PATCH 0759/1179] Badges are crisper as svg [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c87d48867..a7b5d192e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.png?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.png)](https://codeclimate.com/github/rspec/rspec-mocks) +# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.svg?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.svg)](https://codeclimate.com/github/rspec/rspec-mocks) rspec-mocks is a test-double framework for rspec with support for method stubs, fakes, and message expectations on generated test-doubles and real objects alike. From 04e048f04d4142e2f39a0a29302468a77681943d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 25 Nov 2014 00:52:53 -0800 Subject: [PATCH 0760/1179] Validate invocation args for verified null object doubles. --- Changelog.md | 2 ++ lib/rspec/mocks/verifying_double.rb | 2 ++ lib/rspec/mocks/verifying_proxy.rb | 6 ++++-- .../class_double_with_class_loaded_spec.rb | 8 ++++++++ .../instance_double_with_class_loaded_spec.rb | 6 ++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6595e0c01..5175611da 100644 --- a/Changelog.md +++ b/Changelog.md @@ -23,6 +23,8 @@ Bug Fixes: work properly when `SomeConst` has previously been stubbed. `(instance|class)_double("SomeClass")` already worked properly. (Myron Marston, #824) +* Validate invocation args for null object verified doubles. + (Myron Marston, #829) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index f975046b1..f266159ea 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -28,6 +28,8 @@ def method_missing(message, *args, &block) else __mock_proxy.ensure_publicly_implemented(message, self) end + + __mock_proxy.validate_arguments!(message, args) end super diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 9dd9fd207..84ed3c63a 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -78,6 +78,10 @@ def method_reference def visibility_for(method_name) method_reference[method_name].visibility end + + def validate_arguments!(method_name, args) + @method_doubles[method_name].validate_arguments!(args) + end end # @private @@ -131,8 +135,6 @@ def proxy_method_invoked(obj, *args, &block) super end - private - def validate_arguments!(actual_args) @method_reference.with_signature do |signature| verifier = Support::StrictSignatureVerifier.new(signature, actual_args) diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index 331e657d2..6d42164eb 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -107,6 +107,14 @@ module Mocks prevents { o.undefined_method } end + it 'verifies arguments for null objects' do + o = class_double('LoadedClass').as_null_object + + expect { + o.defined_class_method(:too, :many, :args) + }.to raise_error(ArgumentError, "Wrong number of arguments. Expected 0, got 3.") + end + it 'validates `with` args against the method signature when stubbing a method' do dbl = class_double(LoadedClass) prevents(/Wrong number of arguments. Expected 0, got 2./) { diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index c9961e3c7..85b15eacb 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -170,6 +170,12 @@ module Mocks prevents { o.__send__(:undefined_method) } end + it 'verifies arguments' do + expect { + o.defined_instance_method(:too, :many, :args) + }.to raise_error(ArgumentError, "Wrong number of arguments. Expected 0, got 3.") + end + it "includes the double's name in a private method error" do expect { o.rand From 96fa88055c6be26a35f69a6a671b0664690c4eb4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 25 Nov 2014 00:27:43 -0800 Subject: [PATCH 0761/1179] Add a `description` to `receive` matcher. --- lib/rspec/mocks/error_generator.rb | 4 ++-- lib/rspec/mocks/matchers/have_received.rb | 2 +- lib/rspec/mocks/matchers/receive.rb | 26 ++++++++++++++++++++++- lib/rspec/mocks/message_expectation.rb | 7 ++++-- spec/rspec/mocks/matchers/receive_spec.rb | 13 ++++++++++++ 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index cd498fa4a..3b03dae33 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -157,8 +157,8 @@ def method_call_args_description(args) end # @private - def describe_expectation(message, expected_received_count, _actual_received_count, *args) - "have received #{message}#{format_args(*args)} #{count_message(expected_received_count)}" + def describe_expectation(verb, message, expected_received_count, _actual_received_count, *args) + "#{verb} #{message}#{format_args(*args)} #{count_message(expected_received_count)}" end # @private diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 0534ab850..14aeef3e9 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -44,7 +44,7 @@ def failure_message_when_negated end def description - expect.description + expect.description_for("have received") end CONSTRAINTS.each do |expectation| diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index 91760c268..a16b28fb3 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -15,9 +15,13 @@ def name "receive" end + def description + describable.description_for("receive") + end + def setup_expectation(subject, &block) warn_if_any_instance("expect", subject) - setup_mock_proxy_method_substitute(subject, :add_message_expectation, block) + @describable = setup_mock_proxy_method_substitute(subject, :add_message_expectation, block) end alias matches? setup_expectation @@ -60,6 +64,10 @@ def setup_any_instance_allowance(subject, &block) private + def describable + @describable ||= DefaultDescribable.new(@message) + end + def warn_if_any_instance(expression, subject) return unless AnyInstance::Proxy === subject @@ -100,6 +108,22 @@ def move_block_to_last_customization(block) last.block ||= block nil end + + # MessageExpectation objects are able to describe themselves in detail. + # We use this as a fall back when a MessageExpectation is not available. + # @private + class DefaultDescribable + def initialize(message) + @message = message + end + + # This is much simpler for the `any_instance` case than what the + # user may want, but I'm not up for putting a bunch of effort + # into full descriptions for `any_instance` expectations at this point :(. + def description_for(verb) + "#{verb} #{@message}" + end + end end end end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 915951f93..4736d2ba7 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -470,8 +470,11 @@ def expectation_count_type nil end - def description - @error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args) + def description_for(verb) + @error_generator.describe_expectation( + verb, @message, @expected_received_count, + @actual_received_count, *expected_args + ) end def raise_out_of_order_error diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index b896bad60..e53008e53 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -164,6 +164,14 @@ module Mocks expect(e.backtrace.first).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) } end + + it "provides a useful matcher description" do + matcher = receive(:foo).with(:bar).once + wrapped.to matcher + receiver.foo(:bar) + + expect(matcher.description).to start_with("receive foo") + end end shared_examples "an expect syntax negative expectation" do @@ -403,6 +411,11 @@ def receiver.method_missing(*a); end # a poor man's stub... end end + it 'has a description before being matched' do + matcher = receive(:foo) + expect(matcher.description).to eq("receive foo") + end + shared_examples "using rspec-mocks in another test framework" do it 'can use the `expect` syntax' do dbl = double From c5848c50142931e0dbaa1fd1b5360b1d7345d10b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 25 Nov 2014 00:35:12 -0800 Subject: [PATCH 0762/1179] Add a description for `receive_message_chain` matcher. --- lib/rspec/mocks/matchers/receive_message_chain.rb | 14 ++++++++++++++ .../mocks/matchers/receive_message_chain_spec.rb | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index 268a54ded..d831cd3ac 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -22,6 +22,10 @@ def name "receive_message_chain" end + def description + "receive message chain #{formatted_chain}" + end + def setup_allowance(subject, &block) chain = StubChain.stub_chain_on(subject, *@chain, &(@block || block)) replay_customizations(chain) @@ -60,6 +64,16 @@ def replay_customizations(chain) customization.playback_onto(chain) end end + + def formatted_chain + @formatted_chain ||= @chain.map do |part| + if Hash === part + part.keys.first.to_s + else + part.to_s + end + end.join(".") + end end end end diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index 789db3b00..ea2281899 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -212,6 +212,21 @@ module RSpec::Mocks::Matchers expect(o.foo.bar.baz).to eq(3) end + + it "provides a matcher description (when passing a string)" do + matcher = receive_message_chain("foo.bar.bazz") + expect(matcher.description).to eq("receive message chain foo.bar.bazz") + end + + it "provides a matcher description (when passing symbols)" do + matcher = receive_message_chain(:foo, :bar, :bazz) + expect(matcher.description).to eq("receive message chain foo.bar.bazz") + end + + it "provides a matcher description (when passing symbols and a hash)" do + matcher = receive_message_chain(:foo, :bar, :bazz => 3) + expect(matcher.description).to eq("receive message chain foo.bar.bazz") + end end context "when the expect and should syntaxes are enabled" do From 5d8f534db77ca7ca03e0db09a92e88af53e416f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 25 Nov 2014 00:40:45 -0800 Subject: [PATCH 0763/1179] Add a description for `receive_messages` matcher. --- lib/rspec/mocks/matchers/receive_messages.rb | 4 ++++ spec/rspec/mocks/matchers/receive_messages_spec.rb | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index 96962882a..b6316aa45 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -12,6 +12,10 @@ def name "receive_messages" end + def description + "receive messages: #{@message_return_value_hash.inspect}" + end + def setup_expectation(subject) warn_about_block if block_given? each_message_on(proxy_on(subject)) do |host, message, return_value| diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index a645c1d2f..22d088067 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -97,6 +97,12 @@ module Mocks it_behaves_like "complains when given blocks" it_behaves_like "handles partially mocked objects correctly" + + it "provides a matcher description" do + messages = { :a => 1, :b => 2 } + matcher = receive_messages(messages) + expect(matcher.description).to eq("receive messages: #{messages.inspect}") + end end RSpec.describe "expect_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do From 626ecd5366fd1e6c99788bcc7e013e260027f9c6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 25 Nov 2014 00:41:40 -0800 Subject: [PATCH 0764/1179] Add changelog entry. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 5175611da..1d5969804 100644 --- a/Changelog.md +++ b/Changelog.md @@ -23,6 +23,8 @@ Bug Fixes: work properly when `SomeConst` has previously been stubbed. `(instance|class)_double("SomeClass")` already worked properly. (Myron Marston, #824) +* Add a matcher description for `receive`, `receive_messages` and + `receive_message_chain`. (Myron Marston, #828) * Validate invocation args for null object verified doubles. (Myron Marston, #829) From 76b84ddf8c320cbed0251fb902c6974968f73c85 Mon Sep 17 00:00:00 2001 From: Cezary Baginski Date: Mon, 24 Nov 2014 20:19:13 +0100 Subject: [PATCH 0765/1179] allow verifying doubles to use custom names in exceptions --- lib/rspec/mocks/verifying_double.rb | 25 +++--- .../class_double_with_class_loaded_spec.rb | 7 -- .../instance_double_with_class_loaded_spec.rb | 7 -- .../mocks/verifying_doubles/naming_spec.rb | 83 +++++++++++++++++++ spec/support/doubled_classes.rb | 6 ++ 5 files changed, 104 insertions(+), 24 deletions(-) create mode 100644 spec/rspec/mocks/verifying_doubles/naming_spec.rb diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index f975046b1..cc78898b1 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -47,8 +47,17 @@ def send(name, *args, &block) __send__(name, *args, &block) end - def initialize(*args) - super + def initialize(doubled_module, *args) + @doubled_module = doubled_module + + possible_name = args.first + name = if String === possible_name || Symbol === possible_name + args.shift + else + @description + end + + super(name, *args) @__sending_message = nil end end @@ -63,12 +72,8 @@ class InstanceVerifyingDouble include VerifyingDouble def initialize(doubled_module, *args) - @doubled_module = doubled_module - - super( - "#{doubled_module.description} (instance)", - *args - ) + @description = "#{doubled_module.description} (instance)" + super end def __build_mock_proxy(order_group) @@ -95,8 +100,8 @@ def as_stubbed_const(options={}) private def initialize(doubled_module, *args) - @doubled_module = doubled_module - super(doubled_module.description, *args) + @description = doubled_module.description + super end def __build_mock_proxy(order_group) diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index 331e657d2..d0c863f06 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -9,13 +9,6 @@ module Mocks RSpec::Mocks.configuration.verify_doubled_constant_names = true end - it 'includes the double name in errors for unexpected messages' do - o = class_double("LoadedClass") - expect { - o.defined_class_method - }.to fail_matching('Double "LoadedClass"') - end - it 'only allows class methods that exist to be stubbed' do o = class_double('LoadedClass', :defined_class_method => 1) expect(o.defined_class_method).to eq(1) diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index c9961e3c7..25d47c51a 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -9,13 +9,6 @@ module Mocks RSpec::Mocks.configuration.verify_doubled_constant_names = true end - it 'includes the double name in errors for unexpected messages' do - o = instance_double("LoadedClass") - expect { - o.defined_instance_method - }.to fail_matching('Double "LoadedClass (instance)"') - end - it 'only allows instance methods that exist to be stubbed' do o = instance_double('LoadedClass', :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) diff --git a/spec/rspec/mocks/verifying_doubles/naming_spec.rb b/spec/rspec/mocks/verifying_doubles/naming_spec.rb new file mode 100644 index 000000000..806c787dc --- /dev/null +++ b/spec/rspec/mocks/verifying_doubles/naming_spec.rb @@ -0,0 +1,83 @@ +require 'support/doubled_classes' + +module RSpec + module Mocks + RSpec::Matchers.define :fail_expectations_as do |expected| + description { "include a meaningful name in the exception" } + + def error_message_for(verifying_double) + expect(actual).to have_received(:defined_instance_and_class_method) + rescue MockExpectationError, Expectations::ExpectationNotMetError => e + e.message + else + raise("should have failed but did not") + end + + failure_message do |actual| + "expected #{actual.inspect} to fail expecations as:\n" + + " #{expected.inspect}, but failed with:\n" + + " #{@error_message.inspect}" + end + + match do |actual| + @error_message = error_message_for(actual) + @error_message.include?(expected) + end + end + + RSpec.describe 'Verified double naming' do + shared_examples "a named verifying double" do |default_name| + context "when a name is given as a string" do + subject { create_double("LoadedClass", "foo") } + it { is_expected.to fail_expectations_as('Double "foo"') } + end + + context "when a name is given as a symbol" do + subject { create_double("LoadedClass", :foo) } + it { is_expected.to fail_expectations_as('Double :foo') } + end + + context "when no name is given" do + subject { create_double("LoadedClass") } + it { is_expected.to fail_expectations_as("Double \"#{default_name}\"") } + end + end + + describe "instance_double" do + it_behaves_like "a named verifying double", "LoadedClass (instance)" do + alias :create_double :instance_double + end + end + + describe "instance_spy" do + it_behaves_like "a named verifying double", "LoadedClass (instance)" do + alias :create_double :instance_spy + end + end + + describe "class_double" do + it_behaves_like "a named verifying double", "LoadedClass" do + alias :create_double :class_double + end + end + + describe "class_spy" do + it_behaves_like "a named verifying double", "LoadedClass" do + alias :create_double :class_spy + end + end + + describe "object_double" do + it_behaves_like "a named verifying double", "LoadedClass" do + alias :create_double :object_double + end + end + + describe "object_spy" do + it_behaves_like "a named verifying double", "LoadedClass" do + alias :create_double :object_spy + end + end + end + end +end diff --git a/spec/support/doubled_classes.rb b/spec/support/doubled_classes.rb index 1cba2ce28..3405b1a5c 100644 --- a/spec/support/doubled_classes.rb +++ b/spec/support/doubled_classes.rb @@ -19,6 +19,9 @@ def send # fake out! end + def defined_instance_and_class_method + end + protected def defined_protected_class_method @@ -40,6 +43,9 @@ def instance_method_with_two_args(a, b) def instance_method_with_only_defaults(a=1, b=2) end + def defined_instance_and_class_method + end + if required_kw_args_supported? # Need to eval this since it is invalid syntax on earlier rubies. eval <<-RUBY From f95197776b0ed63187f12bf7e3edf31d14a6ea54 Mon Sep 17 00:00:00 2001 From: Cezary Baginski Date: Mon, 24 Nov 2014 20:07:24 +0100 Subject: [PATCH 0766/1179] add class documentation about named verifying doubles --- lib/rspec/mocks/example_methods.rb | 50 +++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index d994b5cd5..258f54f98 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -11,11 +11,11 @@ module ExampleMethods # @overload double() # @overload double(name) - # @param name [String/Symbol] used to clarify intent + # @param name [String/Symbol] name or description to be used in failure messages # @overload double(stubs) # @param stubs (Hash) hash of message/return-value pairs # @overload double(name, stubs) - # @param name [String/Symbol] used to clarify intent + # @param name [String/Symbol] name or description to be used in failure messages # @param stubs (Hash) hash of message/return-value pairs # @return (Double) # @@ -38,9 +38,16 @@ def double(*args) # @overload instance_double(doubled_class) # @param doubled_class [String, Class] + # @overload instance_double(doubled_class, name) + # @param doubled_class [String, Class] + # @param name [String/Symbol] name or description to be used in failure messages # @overload instance_double(doubled_class, stubs) # @param doubled_class [String, Class] # @param stubs [Hash] hash of message/return-value pairs + # @overload instance_double(doubled_class, name, stubs) + # @param doubled_class [String, Class] + # @param name [String/Symbol] name or description to be used in failure messages + # @param stubs [Hash] hash of message/return-value pairs # @return InstanceVerifyingDouble # # Constructs a test double against a specific class. If the given class @@ -54,9 +61,16 @@ def instance_double(doubled_class, *args) # @overload class_double(doubled_class) # @param doubled_class [String, Module] + # @overload class_double(doubled_class, name) + # @param doubled_class [String, Module] + # @param name [String/Symbol] name or description to be used in failure messages # @overload class_double(doubled_class, stubs) # @param doubled_class [String, Module] # @param stubs [Hash] hash of message/return-value pairs + # @overload class_double(doubled_class, name, stubs) + # @param doubled_class [String, Module] + # @param name [String/Symbol] name or description to be used in failure messages + # @param stubs [Hash] hash of message/return-value pairs # @return ClassVerifyingDouble # # Constructs a test double against a specific class. If the given class @@ -70,9 +84,16 @@ def class_double(doubled_class, *args) # @overload object_double(object_or_name) # @param object_or_name [String, Object] + # @overload object_double(object_or_name, name) + # @param object_or_name [String, Object] + # @param name [String/Symbol] name or description to be used in failure messages # @overload object_double(object_or_name, stubs) # @param object_or_name [String, Object] # @param stubs [Hash] hash of message/return-value pairs + # @overload object_double(object_or_name, name, stubs) + # @param object_or_name [String, Object] + # @param name [String/Symbol] name or description to be used in failure messages + # @param stubs [Hash] hash of message/return-value pairs # @return ObjectVerifyingDouble # # Constructs a test double against a specific object. Only the methods @@ -86,11 +107,11 @@ def object_double(object_or_name, *args) # @overload spy() # @overload spy(name) - # @param name [String/Symbol] used to clarify intent + # @param name [String/Symbol] name or description to be used in failure messages # @overload spy(stubs) # @param stubs (Hash) hash of message/return-value pairs # @overload spy(name, stubs) - # @param name [String/Symbol] used to clarify intent + # @param name [String/Symbol] name or description to be used in failure messages # @param stubs (Hash) hash of message/return-value pairs # @return (Double) # @@ -103,9 +124,16 @@ def spy(*args) # @overload instance_spy(doubled_class) # @param doubled_class [String, Class] + # @overload instance_spy(doubled_class, name) + # @param doubled_class [String, Class] + # @param name [String/Symbol] name or description to be used in failure messages # @overload instance_spy(doubled_class, stubs) # @param doubled_class [String, Class] # @param stubs [Hash] hash of message/return-value pairs + # @overload instance_spy(doubled_class, name, stubs) + # @param doubled_class [String, Class] + # @param name [String/Symbol] name or description to be used in failure messages + # @param stubs [Hash] hash of message/return-value pairs # @return InstanceVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` @@ -120,9 +148,16 @@ def instance_spy(*args) # @overload object_spy(object_or_name) # @param object_or_name [String, Object] + # @overload object_spy(object_or_name, name) + # @param object_or_name [String, Class] + # @param name [String/Symbol] name or description to be used in failure messages # @overload object_spy(object_or_name, stubs) # @param object_or_name [String, Object] # @param stubs [Hash] hash of message/return-value pairs + # @overload object_spy(object_or_name, name, stubs) + # @param object_or_name [String, Class] + # @param name [String/Symbol] name or description to be used in failure messages + # @param stubs [Hash] hash of message/return-value pairs # @return ObjectVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` @@ -136,9 +171,16 @@ def object_spy(*args) # @overload class_spy(doubled_class) # @param doubled_class [String, Module] + # @overload class_spy(doubled_class, name) + # @param doubled_class [String, Class] + # @param name [String/Symbol] name or description to be used in failure messages # @overload class_spy(doubled_class, stubs) # @param doubled_class [String, Module] # @param stubs [Hash] hash of message/return-value pairs + # @overload class_spy(doubled_class, name, stubs) + # @param doubled_class [String, Class] + # @param name [String/Symbol] name or description to be used in failure messages + # @param stubs [Hash] hash of message/return-value pairs # @return ClassVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` From b5985e714472a2ad7ce1249dabae3088e583ece4 Mon Sep 17 00:00:00 2001 From: Cezary Baginski Date: Mon, 24 Nov 2014 19:54:20 +0100 Subject: [PATCH 0767/1179] mention naming verifying doubles in README --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index c87d48867..2a04c508f 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,16 @@ isolation without your dependencies loaded while still being able to validate them against real objects. More detail is available in [their documentation](https://github.com/rspec/rspec-mocks/blob/master/features/verifying_doubles). +Verifying doubles can also accept custom identifiers, just like double(), e.g.: + +```ruby +books = [] +books << instance_double("Book", :rspec_book, :pages => 250) +books << instance_double("Book", "(Untitled)", :pages => 5000) + +puts books.inspect # with names, it's clearer which were actually added +``` + ## Method Stubs A method stub is an implementation that returns a pre-determined value. Method From a454e6f1995a83194581bb00d04e29d7ffa4feb1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 28 Nov 2014 21:14:46 -0800 Subject: [PATCH 0768/1179] Add changelog entry for #826. [ci skip] --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 1d5969804..57ca0d8e8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,9 @@ Enhancements: number of args at any point in an arg list. (Myron Marston, #786) * Print diffs when arguments in mock expectations are mismatched. (Sam Phippen, #751) +* Support names for verified doubles (`instance_double`, `instance_spy`, + `class_double`, `class_spy`, `object_double`, `object_spy`). (Cezary + Baginski, #826) Bug Fixes: From 689878e7f51c616de14527dcc72a8e6187cece3c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 29 Nov 2014 23:32:12 -0800 Subject: [PATCH 0769/1179] Make `RSpec::Mocks::Constant.original(name)` handle invalid names gracefully. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since this API returns an object that provides lots of details about the constant (rather than just the original value), it's not meant to raise errors when given constant names that can’t resolve or are invalid -- instead the returned object indicates that the provided name was invalid. --- Changelog.md | 3 +++ lib/rspec/mocks/mutate_const.rb | 27 +++++++++++++++++++-------- spec/rspec/mocks/mutate_const_spec.rb | 23 +++++++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index 57ca0d8e8..da1a56ef3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -30,6 +30,9 @@ Bug Fixes: `receive_message_chain`. (Myron Marston, #828) * Validate invocation args for null object verified doubles. (Myron Marston, #829) +* Fix `RSpec::Mocks::Constant.original` when called with an invalid + constant to return an object indicating the constant name is invalid, + rather than blowing up. (Myron Marston, #833) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index cdbade080..7cb1c9278 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -13,6 +13,8 @@ def initialize(name) @previously_defined = false @stubbed = false @hidden = false + @valid_name = true + yield self if block_given? end # @return [String] The fully qualified name of the constant. @@ -24,7 +26,7 @@ def initialize(name) attr_accessor :original_value # @private - attr_writer :previously_defined, :stubbed, :hidden + attr_writer :previously_defined, :stubbed, :hidden, :valid_name # @return [Boolean] Whether or not the constant was defined # before the current example. @@ -50,6 +52,12 @@ def hidden? @hidden end + # @return [Boolean] Whether or not the provided constant name + # is a valid Ruby constant name. + def valid_name? + @valid_name + end + # The default `to_s` isn't very useful, so a custom version is provided. def to_s "#<#{self.class.name} #{name}>" @@ -58,13 +66,16 @@ def to_s # @private def self.unmutated(name) - const = new(name) - const.previously_defined = recursive_const_defined?(name) - const.stubbed = false - const.hidden = false - const.original_value = recursive_const_get(name) if const.previously_defined? - - const + previously_defined = recursive_const_defined?(name) + rescue NameError + new(name) do |c| + c.valid_name = false + end + else + new(name) do |const| + const.previously_defined = previously_defined + const.original_value = recursive_const_get(name) if previously_defined + end end # Queries rspec-mocks to find out information about the named constant. diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index b80cb7de6..bb9bb7a29 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -439,6 +439,7 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has not been mutated") { expect(const).not_to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } @@ -451,6 +452,7 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } @@ -463,6 +465,7 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::Undefined") } it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } @@ -475,6 +478,7 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::UndefinedModule") } it("exposes its name") { expect(const.name).to eq("TestClass::UndefinedModule") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } @@ -486,6 +490,7 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::Undefined") } it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has not been mutated") { expect(const).not_to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } @@ -499,6 +504,7 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } @@ -512,6 +518,7 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::Undefined") } it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } @@ -524,6 +531,7 @@ def change_const_value_to(value) let(:const) { Constant.original("SomeUndefinedConst") } it("exposes its name") { expect(const.name).to eq("SomeUndefinedConst") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously undefined") { expect(const).not_to be_previously_defined } it("indicates it has not been mutated") { expect(const).not_to be_mutated } it("indicates it has not not been stubbed") { expect(const).not_to be_stubbed } @@ -536,6 +544,7 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } @@ -549,12 +558,26 @@ def change_const_value_to(value) let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } + it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } it("indicates it has been hidden") { expect(const).to be_hidden } it("exposes its original value") { expect(const.original_value).to eq(:m) } end + + context "for an invalid const name (such as an anonymous module's `inspect` output)" do + let(:mod) { Module.new } + let(:const) { Constant.original(mod.inspect) } + + it("exposes the provided string as the name") { expect(const.name).to eq(mod.inspect) } + it("indicates the name is invalid") { expect(const).not_to be_valid_name } + it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } + it("indicates it has not been mutated") { expect(const).not_to be_mutated } + it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } + it("indicates it has not been hidden") { expect(const).not_to be_hidden } + it("returns nil for its original value") { expect(const.original_value).to be_nil } + end end end end From addf8949b9e263fe2dcd526ec4715a736fdf9b7d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Dec 2014 12:33:45 -0800 Subject: [PATCH 0770/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 6 +++--- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4c9839194..d99160647 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 307c1c339..f86a4388e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 47d1d045b..d33088079 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -20,11 +20,11 @@ install: - gem --version - gem install bundler - bundler --version - - bundle install + - bundle install --standalone --binstubs - cinst ansicon test_script: - - bundle exec rspec + - bin\rspec environment: matrix: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 131da0b0a..f56157b06 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 21778f9f7..fa6776771 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 5e6eb4ad0..e5e9d17cf 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 4f4764d0d..f6197695e 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 82da2bc3e..9a8451182 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 85bda905d66aba967686e5ede1b496f1eebc8ef1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Dec 2014 14:40:37 -0800 Subject: [PATCH 0771/1179] Revert "Updated travis build scripts (from rspec-dev)" This reverts commit addf8949b9e263fe2dcd526ec4715a736fdf9b7d. [ci skip] --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 6 +++--- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index d99160647..4c9839194 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index f86a4388e..307c1c339 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index d33088079..47d1d045b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -20,11 +20,11 @@ install: - gem --version - gem install bundler - bundler --version - - bundle install --standalone --binstubs + - bundle install - cinst ansicon test_script: - - bin\rspec + - bundle exec rspec environment: matrix: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f56157b06..131da0b0a 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index fa6776771..21778f9f7 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index e5e9d17cf..5e6eb4ad0 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index f6197695e..4f4764d0d 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 9a8451182..82da2bc3e 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 3a9efe0bbd6a3dddae743213169341e679cbc15b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Dec 2014 12:46:26 -0800 Subject: [PATCH 0772/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 5 +++-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4c9839194..0180db0c2 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 307c1c339..f6e810cfc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 47d1d045b..288c20522 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -18,7 +18,8 @@ install: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - ruby --version - gem --version - - gem install bundler + # We lock to 1.7.7 to avoid warnings from 1.7.8 + - gem install bundler -v=1.7.7 - bundler --version - bundle install - cinst ansicon diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 131da0b0a..5f0a2e4ec 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 21778f9f7..7eb77aba8 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 5e6eb4ad0..2cf429a83 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 4f4764d0d..8a2f2eb1a 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 82da2bc3e..1fec63caf 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 1ec04ed385614c855669b908965c41671ef8f137 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 9 Dec 2014 08:48:59 -0800 Subject: [PATCH 0773/1179] Rework spec. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before, it mutated the message expectation after it had already been used. This was just done as a matter of convenience in the spec, and wasn’t the core point of it. (Honestly, I didn’t think about mutation-after-use at the time). Now, it still verifies that it can be overridden in all the same ways, but doesn’t use the expectation before overriding it. This preserves the important behavior while opening the door for #778. --- ...bining_implementation_instructions_spec.rb | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index e2a2c1c24..02be83a11 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -124,21 +124,25 @@ def verify_combined_implementation expect(block_called).to be_truthy end - it 'allows the terminal action to be overriden' do - dbl = double - stubbed_double = allow(dbl).to receive(:foo) - - stubbed_double.and_return(1) - expect(dbl.foo).to eq(1) - - stubbed_double.and_return(3) - expect(dbl.foo).to eq(3) + describe "a double that already has a terminal `and_return(x)` action" do + let(:dbl) { double } + let(:stubbed_double) { allow(dbl).to receive(:foo) } + before { stubbed_double.and_return(1) } + + it 'allows the terminal action to be overriden to `and_return(y)`' do + stubbed_double.and_return(3) + expect(dbl.foo).to eq(3) + end - stubbed_double.and_raise("boom") - expect { dbl.foo }.to raise_error("boom") + it 'allows the terminal action to be overriden to `and_raise(y)`' do + stubbed_double.and_raise("boom") + expect { dbl.foo }.to raise_error("boom") + end - stubbed_double.and_throw(:bar) - expect { dbl.foo }.to throw_symbol(:bar) + it 'allows the terminal action to be overriden to `and_throw(y)`' do + stubbed_double.and_throw(:bar) + expect { dbl.foo }.to throw_symbol(:bar) + end end it 'allows the inner implementation block to be overriden' do From 786b262d6f1e04d6d04d26b0276fae02d4377d95 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 16 Nov 2014 09:40:20 -0500 Subject: [PATCH 0774/1179] Make the collection matchers composable with other matchers. Fixes #801 --- lib/rspec/mocks/argument_matchers.rb | 32 +++++++++++++++++-- lib/rspec/mocks/error_generator.rb | 6 +--- .../mocks/array_including_matcher_spec.rb | 23 +++++++++++++ spec/rspec/mocks/diffing_spec.rb | 12 +------ spec/rspec/mocks/error_generator_spec.rb | 6 +--- .../mocks/hash_including_matcher_spec.rb | 6 ++++ spec/spec_helper.rb | 24 ++++++++++++++ 7 files changed, 85 insertions(+), 24 deletions(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 3e4a4ccea..5114a0cb2 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -198,7 +198,20 @@ def ===(predicate, actual) end def description(name) - "#{name}(#{@expected.inspect.sub(/^\{/, "").sub(/\}$/, "")})" + "#{name}(#{formatted_expected_hash.inspect.sub(/^\{/, "").sub(/\}$/, "")})" + end + + private + + def formatted_expected_hash + Hash[ + @expected.map do |k, v| + k = RSpec::Support.rspec_description_for_object(k) + v = RSpec::Support.rspec_description_for_object(v) + + [k, v] + end + ] end end @@ -231,11 +244,24 @@ def initialize(expected) end def ===(actual) - Set.new(actual).superset?(Set.new(@expected)) + actual = actual.uniq + @expected.uniq.all? do |expected_element| + actual.any? do |actual_element| + RSpec::Support::FuzzyMatcher.values_match?(expected_element, actual_element) + end + end end def description - "array_including(#{@expected.join(", ")})" + "array_including(#{formatted_expected_values})" + end + + private + + def formatted_expected_values + @expected.map do |x| + RSpec::Support.rspec_description_for_object(x) + end.join(", ") end end diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 3b03dae33..3503d377d 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -214,11 +214,7 @@ def unexpected_arguments_message(expected_args_string, actual_args_string) def diff_message(expected_args, actual_args) formatted_expected_args = expected_args.map do |x| - if arg_has_valid_description?(x) - x.description - else - x - end + RSpec::Support.rspec_description_for_object(x) end formatted_expected_args, actual_args = unpack_string_args(formatted_expected_args, actual_args) diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb index 9dac97b91..1430f0391 100644 --- a/spec/rspec/mocks/array_including_matcher_spec.rb +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -6,6 +6,12 @@ module ArgumentMatchers expect(ArrayIncludingMatcher.new([1, 2, 3]).description).to eq "array_including(1, 2, 3)" end + it "describes passed matchers" do + description = array_including(fake_matcher(Object.new)).description + + expect(description).to include(MatcherHelpers.fake_matcher_description) + end + context "passing" do it "matches the same array" do expect(array_including(1, 2, 3)).to be === [1, 2, 3] @@ -26,12 +32,29 @@ module ArgumentMatchers it "works with duplicates in actual" do expect(array_including(1, 2, 3)).to be === [1, 1, 2, 3] end + + it "is composable with other matchers" do + klass = Class.new + dbl = double + expect(dbl).to receive(:a_message).with(3, array_including(instance_of(klass))) + dbl.a_message(3, [1, klass.new, 4]) + end end context "failing" do it "fails when not all the entries in the expected are present" do expect(array_including(1,2,3,4,5)).not_to be === [1,2] end + + it "fails when passed a composed matcher is pased and not satisfied" do + with_unfulfilled_double do |dbl| + expect { + klass = Class.new + expect(dbl).to receive(:a_message).with(3, array_including(instance_of(klass))) + dbl.a_message(3, [1, 4]) + }.to fail_with(/expected: \(3, array_including\(an_instance_of\(\)\)\)/) + end + end end end end diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index 8ddb5fc6e..4fb41c2b4 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -6,12 +6,6 @@ allow(RSpec::Mocks.configuration).to receive(:color?).and_return(false) end - def with_unfulfilled_double - d = double("double") - yield d - reset d - end - context "with a non matcher object" do it "does not print a diff when single line arguments are mismatched" do with_unfulfilled_double do |d| @@ -97,15 +91,11 @@ def with_unfulfilled_double end context "with a matcher object" do - matcher :fake_matcher do - match { false } - end - context "that defines #description" do it "uses the object's description" do with_unfulfilled_double do |d| - collab = fake_matcher + collab = fake_matcher(Object.new) collab_description = collab.description expect(d).to receive(:foo).with(collab) diff --git a/spec/rspec/mocks/error_generator_spec.rb b/spec/rspec/mocks/error_generator_spec.rb index e196c7d7e..92aa0b931 100644 --- a/spec/rspec/mocks/error_generator_spec.rb +++ b/spec/rspec/mocks/error_generator_spec.rb @@ -18,14 +18,10 @@ def unexpected_failure_message_for(object_description) end context "on matcher objects" do - matcher :fake_matcher do - match { false } - end - context "that define description" do it "uses the object's description" do d = double(:double) - o = fake_matcher + o = fake_matcher(Object.new) expect { d.bees(o) }.to raise_error(unexpected_failure_message_for(o.description)) diff --git a/spec/rspec/mocks/hash_including_matcher_spec.rb b/spec/rspec/mocks/hash_including_matcher_spec.rb index 9882cba31..0874366f9 100644 --- a/spec/rspec/mocks/hash_including_matcher_spec.rb +++ b/spec/rspec/mocks/hash_including_matcher_spec.rb @@ -7,6 +7,12 @@ module ArgumentMatchers expect(HashIncludingMatcher.new(:a => 1).description).to eq "hash_including(:a=>1)" end + it "describes passed matchers" do + description = hash_including(:foo => fake_matcher(Object.new)).description + + expect(description).to include(MatcherHelpers.fake_matcher_description) + end + describe "passing" do it "matches the same hash" do expect(hash_including(:a => 1)).to be === {:a => 1} diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 32ebb09a6..c461eed77 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -39,6 +39,12 @@ def verify_all def reset_all RSpec::Mocks.space.reset_all end + + def with_unfulfilled_double + d = double("double") + yield d + reset d + end end module VerificationHelpers @@ -48,6 +54,23 @@ def prevents(msg = //, &block) end end +module MatcherHelpers + def self.fake_matcher_description + "fake_matcher_description" + end + + extend RSpec::Matchers::DSL + + matcher :fake_matcher do |expected| + match {|actual| actual == expected} + + description do + MatcherHelpers.fake_matcher_description + end + end +end + + require 'rspec/support/spec' RSpec.configure do |config| @@ -76,6 +99,7 @@ def prevents(msg = //, &block) end config.include VerifyAndResetHelpers + config.include MatcherHelpers config.include VerificationHelpers config.extend RSpec::Support::RubyFeatures config.include RSpec::Support::RubyFeatures From 1acff37fcf247360cabb066f4133642807fcf381 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 10 Dec 2014 11:24:56 +0000 Subject: [PATCH 0775/1179] Changelog entry for #819 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index da1a56ef3..92ea4f085 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,8 @@ Enhancements: * Support names for verified doubles (`instance_double`, `instance_spy`, `class_double`, `class_spy`, `object_double`, `object_spy`). (Cezary Baginski, #826) +* Make `array_including` and `hash_including` argument matchers composable. + (Sam Phippen, #819) Bug Fixes: From de9eef79da543a4c498097462eee240d82fe860f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 17 Dec 2014 17:12:37 -0800 Subject: [PATCH 0776/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 5 ++++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 0180db0c2..48a9e9423 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index f6e810cfc..032ded0db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,9 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby sudo: false +cache: bundler before_install: - "script/clone_all_rspec_repos" # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 @@ -21,6 +22,7 @@ rvm: - 2.1.3 - 2.1.4 - 2.1.5 + - 2.2 - ruby-head - ree - jruby-18mode @@ -35,4 +37,5 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx + - rvm: 2.2 fast_finish: true diff --git a/appveyor.yml b/appveyor.yml index 288c20522..cc6d36b92 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 5f0a2e4ec..9766801b2 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 7eb77aba8..e52a7f98f 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 2cf429a83..92d0feee6 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 8a2f2eb1a..09f6d75a6 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 1fec63caf..f3256eff4 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From ac99800b9d47793deaacfbc7e180e9814f1de30d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 09:27:23 -0800 Subject: [PATCH 0777/1179] =?UTF-8?q?Fix=20standalone=20so=20it=20doesn?= =?UTF-8?q?=E2=80=99t=20infect=20other=20objects.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also makes `extend RSpec::Mocks::ExampleMethods` work properly. Fixes #845. --- Changelog.md | 8 +++++++ features/outside_rspec/standalone.feature | 2 +- lib/rspec/mocks/example_methods.rb | 7 ++++++ lib/rspec/mocks/standalone.rb | 2 +- spec/rspec/mocks/example_methods_spec.rb | 27 +++++++++++++++++++++++ spec/rspec/mocks/standalone_spec.rb | 27 +++++++++++++++++++++++ 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 spec/rspec/mocks/standalone_spec.rb diff --git a/Changelog.md b/Changelog.md index 92ea4f085..5ecb672d8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -35,6 +35,14 @@ Bug Fixes: * Fix `RSpec::Mocks::Constant.original` when called with an invalid constant to return an object indicating the constant name is invalid, rather than blowing up. (Myron Marston, #833) +* Make `extend RSpec::Mocks::ExampleMethods` on any object work properly + to add the rspec-mocks API to that object. Previously, `expect` would + be undefined. (Myron Marston, #846) +* Fix `require 'rspec/mocks/standalone'` so that it only affects `main` + and not every object. It's really only intended to be used in a REPL + like IRB, but some gems have loaded it, thinking it needs to be loaded + when using rspec-mocks outside the context of rspec-core. + (Myron Marston, #846) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) diff --git a/features/outside_rspec/standalone.feature b/features/outside_rspec/standalone.feature index c324e27d3..862e470e3 100644 --- a/features/outside_rspec/standalone.feature +++ b/features/outside_rspec/standalone.feature @@ -1,7 +1,7 @@ Feature: Standalone `require "rspec/mocks/standalone"` to expose the API at the top level (e.g. `main`) outside - the RSpec environment. This is especially useful for exploring rspec-mocks in irb. + the RSpec environment in a REPL like IRB or in a one-off script. Scenario: Allow a message outside RSpec Given a file named "example.rb" with: diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 258f54f98..c001a95d0 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -392,6 +392,13 @@ def self.included(klass) end end + # @private + def self.extended(object) + # This gets extended in so that if `RSpec::Matchers` is included in + # `klass` later, it's definition of `expect` will take precedence. + object.extend ExpectHost unless object.respond_to?(:expect) + end + # @private def self.declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && diff --git a/lib/rspec/mocks/standalone.rb b/lib/rspec/mocks/standalone.rb index c586712a1..74317b01b 100644 --- a/lib/rspec/mocks/standalone.rb +++ b/lib/rspec/mocks/standalone.rb @@ -1,3 +1,3 @@ require 'rspec/mocks' -include RSpec::Mocks::ExampleMethods +extend RSpec::Mocks::ExampleMethods RSpec::Mocks.setup diff --git a/spec/rspec/mocks/example_methods_spec.rb b/spec/rspec/mocks/example_methods_spec.rb index 157e5883f..59c526524 100644 --- a/spec/rspec/mocks/example_methods_spec.rb +++ b/spec/rspec/mocks/example_methods_spec.rb @@ -6,6 +6,33 @@ module Mocks 'them' do expect(ExampleMethods.private_instance_methods).to eq([]) end + + def test_extend_on_new_object(*to_extend, &block) + host = Object.new + to_extend.each { |mod| host.extend mod } + host.instance_eval do + dbl = double + expect(dbl).to receive(:foo).at_least(:once).and_return(1) + dbl.foo + instance_exec(dbl, &block) if block + end + end + + it 'works properly when extended onto an object' do + test_extend_on_new_object ExampleMethods + end + + it 'works properly when extended onto an object that has previous extended `RSpec::Matchers`' do + test_extend_on_new_object RSpec::Matchers, ExampleMethods do |dbl| + expect(dbl.foo).to eq(1) + end + end + + it 'works properly when extended onto an object that later extends `RSpec::Matchers`' do + test_extend_on_new_object ExampleMethods, RSpec::Matchers do |dbl| + expect(dbl.foo).to eq(1) + end + end end end end diff --git a/spec/rspec/mocks/standalone_spec.rb b/spec/rspec/mocks/standalone_spec.rb new file mode 100644 index 000000000..bb5822c6e --- /dev/null +++ b/spec/rspec/mocks/standalone_spec.rb @@ -0,0 +1,27 @@ +require 'rspec/support/spec/in_sub_process' +main = self + +RSpec.describe "Loading rspec/mocks/standalone" do + include RSpec::Support::InSubProcess + + it "exposes the RSpec::Mocks API on `main`" do + in_sub_process do + require 'rspec/mocks/standalone' + main.instance_eval do + dbl = double + expect(dbl).to receive(:foo) + dbl.foo + RSpec::Mocks.verify + RSpec::Mocks.teardown + end + end + end + + it "does not infect other objects with the RSpec::Mocks API" do + in_sub_process do + require 'rspec/mocks/standalone' + object = Object.new + expect(object).not_to respond_to(:double, :expect) + end + end +end From 0eb94623ee240e193e8bd4c831eb6d8d7ebeebec Mon Sep 17 00:00:00 2001 From: Melanie Gilman Date: Mon, 24 Nov 2014 13:37:26 -0500 Subject: [PATCH 0778/1179] Prevent invoked expectations from being modified after they are invoked. --- lib/rspec/mocks/error_generator.rb | 5 ++++ lib/rspec/mocks/message_expectation.rb | 22 +++++++++++++++ ...bining_implementation_instructions_spec.rb | 18 ++++++------- .../modifying_invoked_expectations_spec.rb | 27 +++++++++++++++++++ 4 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 spec/rspec/mocks/modifying_invoked_expectations_spec.rb diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 3503d377d..3afbf688a 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -10,6 +10,11 @@ module Mocks # Raised when doubles or partial doubles are used outside of the per-test lifecycle. OutsideOfExampleError = Class.new(StandardError) + # Raised when an expectation customization method (e.g. `with`, + # `and_return`) is called on a message expectation which has already been + # invoked. + MockExpectationAlreadyInvokedError = Class.new(Exception) + # @private UnsupportedMatcherError = Class.new(StandardError) # @private diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 4736d2ba7..43d8479b9 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -65,6 +65,7 @@ class MessageExpectation # counter.count # => 3 # # etc def and_return(first_value, *values) + raise_already_invoked_error_if_necessary(__method__) if negative? raise "`and_return` is not supported with negative message expectations" end @@ -142,6 +143,7 @@ def and_wrap_original(&block) # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive") # allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz)) def and_raise(exception=RuntimeError, message=nil) + raise_already_invoked_error_if_necessary(__method__) if exception.respond_to?(:exception) exception = message ? exception.exception(message) : exception.exception end @@ -161,6 +163,7 @@ def and_raise(exception=RuntimeError, message=nil) # allow(car).to receive(:go).and_throw(:out_of_gas) # allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1) def and_throw(*args) + raise_already_invoked_error_if_necessary(__method__) self.terminal_implementation_action = Proc.new { throw(*args) } nil end @@ -172,6 +175,7 @@ def and_throw(*args) # @example # stream.stub(:open).and_yield(StringIO.new) def and_yield(*args, &block) + raise_already_invoked_error_if_necessary(__method__) yield @eval_context = Object.new if block @args_to_yield << args self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator) @@ -188,6 +192,7 @@ def and_yield(*args, &block) # @example # expect(dealer).to receive(:deal_card).exactly(10).times def exactly(n, &block) + raise_already_invoked_error_if_necessary(__method__) self.inner_implementation_action = block set_expected_received_count :exactly, n self @@ -200,6 +205,7 @@ def exactly(n, &block) # @example # expect(dealer).to receive(:deal_card).at_least(9).times def at_least(n, &block) + raise_already_invoked_error_if_necessary(__method__) set_expected_received_count :at_least, n if n == 0 @@ -218,6 +224,7 @@ def at_least(n, &block) # @example # expect(dealer).to receive(:deal_card).at_most(10).times def at_most(n, &block) + raise_already_invoked_error_if_necessary(__method__) self.inner_implementation_action = block set_expected_received_count :at_most, n self @@ -307,6 +314,7 @@ def thrice(&block) # cart.add(Book.new(:isbn => 1934356379)) # # => passes def with(*args, &block) + raise_already_invoked_error_if_necessary(__method__) if args.empty? raise ArgumentError, "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments." @@ -528,6 +536,20 @@ def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, end end + def has_been_invoked? + @actual_received_count > 0 + end + + def raise_already_invoked_error_if_necessary(calling_customization) + return unless has_been_invoked? + + error_message = "The message expectation for #{orig_object.inspect}.#{message} has already been invoked " \ + "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \ + "customizations must be applied before it is used for the first time." + + raise MockExpectationAlreadyInvokedError, error_message + end + def failed_fast? @failed_fast end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 02be83a11..d441784a2 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -145,16 +145,16 @@ def verify_combined_implementation end end - it 'allows the inner implementation block to be overriden' do - allow(RSpec).to receive(:warning) - dbl = double - stubbed_double = allow(dbl).to receive(:foo) - - stubbed_double.with(:arg) { :with_block } - expect(dbl.foo(:arg)).to eq(:with_block) + describe "a double that already has a terminal block action" do + let(:dbl) { double } + let(:stubbed_double) { allow(dbl).to receive(:foo) } - stubbed_double.at_least(:once) { :at_least_block } - expect(dbl.foo(:arg)).to eq(:at_least_block) + it "allows the block action to be overridden" do + allow(RSpec).to receive(:warning) + stubbed_double.with(:arg) { :with_block } + stubbed_double.at_least(:once) { :at_least_block } + expect(dbl.foo(:arg)).to eq(:at_least_block) + end end it 'warns when the inner implementation block is overriden' do diff --git a/spec/rspec/mocks/modifying_invoked_expectations_spec.rb b/spec/rspec/mocks/modifying_invoked_expectations_spec.rb new file mode 100644 index 000000000..988608ea9 --- /dev/null +++ b/spec/rspec/mocks/modifying_invoked_expectations_spec.rb @@ -0,0 +1,27 @@ +require "spec_helper" + +RSpec.describe "Modifying invoked expectations" do + shared_examples_for "a customization on an invoked expectation" do |customization_method, *args| + it "raises when the #{customization_method} method is called, indicating the expectation has already been invoked" do + dbl = double + msg_expectation = expect(dbl).to receive(:foo) + expect(dbl.foo).to eq(nil) + + expect { + msg_expectation.__send__(customization_method, *args) + }.to raise_error( + RSpec::Mocks::MockExpectationAlreadyInvokedError, + a_string_including(dbl.inspect, "foo", customization_method.to_s) + ) + end + end + + it_behaves_like "a customization on an invoked expectation", :with, :some_arg + it_behaves_like "a customization on an invoked expectation", :and_return, 1 + it_behaves_like "a customization on an invoked expectation", :and_raise, "boom" + it_behaves_like "a customization on an invoked expectation", :and_throw, :symbol + it_behaves_like "a customization on an invoked expectation", :and_yield, 1 + it_behaves_like "a customization on an invoked expectation", :exactly, :once + it_behaves_like "a customization on an invoked expectation", :at_least, :once + it_behaves_like "a customization on an invoked expectation", :at_most, :once +end From 3f1124e7994c8b34d97e3853c80b9f8ba9621e3b Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 10 Dec 2014 14:05:28 +0000 Subject: [PATCH 0779/1179] Changelog entry for #837 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 5ecb672d8..257eb4019 100644 --- a/Changelog.md +++ b/Changelog.md @@ -43,6 +43,8 @@ Bug Fixes: like IRB, but some gems have loaded it, thinking it needs to be loaded when using rspec-mocks outside the context of rspec-core. (Myron Marston, #846) +* Prevent message expectations from being modified by customization methods + (e.g. `with`) after they have been invoked. (Sam Phippen and Melanie Gilman, #837) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) From f8c1fd83068428aedbe78b284074f9b14906e169 Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Mon, 22 Dec 2014 17:34:48 -0800 Subject: [PATCH 0780/1179] Fix typo on README line 380. * change 'sematically' to 'semantically' * remove some whitespace from line 55. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 657b8631f..325b2ae04 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ rspec-mocks supports 3 forms for declaring method stubs: allow(book).to receive(:title) { "The RSpec Book" } allow(book).to receive(:title).and_return("The RSpec Book") allow(book).to receive_messages( - :title => "The RSpec Book", + :title => "The RSpec Book", :subtitle => "Behaviour-Driven Development with RSpec, Cucumber, and Friends") ``` @@ -377,7 +377,7 @@ general we discourage its use for a number of reasons: * The `rspec-mocks` API is designed for individual object instances, but this feature operates on entire classes of objects. As a result there are some - sematically confusing edge cases. For example in + semantically confusing edge cases. For example in `expect_any_instance_of(Widget).to receive(:name).twice` it isn't clear whether each specific instance is expected to receive `name` twice, or if two receives total are expected. (It's the former.) From 7f702387802e949b64a4986d69883a23a444a6ba Mon Sep 17 00:00:00 2001 From: Ben Moss Date: Sat, 27 Dec 2014 13:03:38 -0500 Subject: [PATCH 0781/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 5 +++-- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 48a9e9423..85ab01e42 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 032ded0db..a6b6fba9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index cc6d36b92..e784692db 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 9766801b2..8ec4e6734 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index e52a7f98f..848756328 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 92d0feee6..5508da061 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 09f6d75a6..dbdd5d3c6 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index f3256eff4..8d1578e99 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: @@ -52,7 +52,8 @@ fold() { travis_time_start fi - "$@" || status=$? + "$@" + status=$? [ -z "$TRAVIS" ] || travis_time_finish From c152810e4e00f1fe7a5fc4ba5e6f9d8e9e2593e9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 26 Dec 2014 23:36:21 -0800 Subject: [PATCH 0782/1179] YAML::ENGINE has been removed from Ruby 2.2. --- spec/rspec/mocks/serialization_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/serialization_spec.rb b/spec/rspec/mocks/serialization_spec.rb index af289062e..169c529a7 100644 --- a/spec/rspec/mocks/serialization_spec.rb +++ b/spec/rspec/mocks/serialization_spec.rb @@ -62,10 +62,10 @@ def set_stub before(:each) { ::YAML::ENGINE.yamler = 'syck' } around(:each) { |example| with_isolated_stderr(&example) } it_behaves_like 'normal YAML serialization' - end + end if defined?(::YAML::ENGINE) context 'using Psych as the YAML engine' do - before(:each) { ::YAML::ENGINE.yamler = 'psych' } + before(:each) { ::YAML::ENGINE.yamler = 'psych' } if defined?(::YAML::ENGINE) it_behaves_like 'normal YAML serialization' end else From 9de9d60f29f12ae288a83cab2dca595a5af07fe7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 27 Dec 2014 00:29:17 -0800 Subject: [PATCH 0783/1179] Address MRI 2.2 warnings (possible reference to past scope). From https://github.com/ruby/ruby/commit/f5f6218a23fdbe45d1a80c202a131f02c3eef0ce. It causes 280 warnings in our spec suite :(. The fix is to change the names of variables and/or `let` definitions so they do not clash. See https://bugs.ruby-lang.org/issues/10661 for more discussion. --- spec/rspec/mocks/and_call_original_spec.rb | 14 +- spec/rspec/mocks/and_yield_spec.rb | 16 +- spec/rspec/mocks/any_instance_spec.rb | 28 ++-- ...bining_implementation_instructions_spec.rb | 16 +- spec/rspec/mocks/double_spec.rb | 4 +- .../mocks/matchers/have_received_spec.rb | 144 +++++++++--------- spec/rspec/mocks/partial_double_spec.rb | 8 +- spec/rspec/mocks/space_spec.rb | 16 +- .../stubbed_message_expectations_spec.rb | 50 +++--- spec/rspec/mocks/to_ary_spec.rb | 4 +- .../expected_arg_verification_spec.rb | 6 +- .../instance_double_with_class_loaded_spec.rb | 44 +++--- ...tance_double_with_class_not_loaded_spec.rb | 10 +- 13 files changed, 180 insertions(+), 180 deletions(-) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 398002cd6..fdc0b2a5b 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -81,10 +81,10 @@ def instance.foo; :bar; end end it 'works for SimpleDelegator subclasses', :if => (RUBY_VERSION.to_f > 1.8) do - instance = Class.new(SimpleDelegator).new(1) - def instance.foo; :bar; end - expect(instance).to receive(:foo).and_call_original - expect(instance.foo).to eq(:bar) + inst = Class.new(SimpleDelegator).new(1) + def inst.foo; :bar; end + expect(inst).to receive(:foo).and_call_original + expect(inst.foo).to eq(:bar) end end @@ -162,14 +162,14 @@ def instance.foo; :bar; end end it 'works for aliased methods' do - klass = Class.new do + klazz = Class.new do class << self alias alternate_new new end end - expect(klass).to receive(:alternate_new).and_call_original - expect(klass.alternate_new).to be_an_instance_of(klass) + expect(klazz).to receive(:alternate_new).and_call_original + expect(klazz.alternate_new).to be_an_instance_of(klazz) end context 'on an object that defines method_missing' do diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index af22adef9..ab974767c 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -90,8 +90,8 @@ expect(yielded_arg).to receive(:bar) end - obj.method_that_accepts_a_block do |obj| - obj.bar + obj.method_that_accepts_a_block do |object| + object.bar foo end @@ -103,12 +103,12 @@ it "yields the default argument when the argument is not given" do pending "Not sure how to achieve this yet. See rspec/rspec-mocks#714 and rspec/rspec-support#85." default_arg = Object.new - obj = Object.new + object = Object.new - allow(obj).to receive(:a_message).and_yield + allow(object).to receive(:a_message).and_yield expect(default_arg).to receive(:bar) - eval("obj.a_message { |receiver=default_arg| receiver.bar }") + eval("object.a_message { |receiver=default_arg| receiver.bar }") end it "yields given argument when the argument is given" do @@ -116,12 +116,12 @@ allow(default_arg).to receive(:bar) given_arg = Object.new - obj = Object.new + object = Object.new - allow(obj).to receive(:a_message).and_yield(given_arg) + allow(object).to receive(:a_message).and_yield(given_arg) expect(given_arg).to receive(:bar) - eval("obj.a_message { |receiver=default_arg| receiver.bar }") + eval("object.a_message { |receiver=default_arg| receiver.bar }") end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index e427aace1..6555e61e7 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -695,22 +695,22 @@ def inspect end it 'works with a BasicObject subclass that mixes in Kernel', :if => defined?(BasicObject) do - klass = Class.new(BasicObject) do + klazz = Class.new(BasicObject) do include ::Kernel def foo; end end - expect_any_instance_of(klass).to receive(:foo) - klass.new.foo + expect_any_instance_of(klazz).to receive(:foo) + klazz.new.foo end it 'works with a SimpleDelegator subclass', :if => (RUBY_VERSION.to_f > 1.8) do - klass = Class.new(SimpleDelegator) do + klazz = Class.new(SimpleDelegator) do def foo; end end - expect_any_instance_of(klass).to receive(:foo) - klass.new(Object.new).foo + expect_any_instance_of(klazz).to receive(:foo) + klazz.new(Object.new).foo end context "with argument matching" do @@ -1094,13 +1094,13 @@ def foo; end end it "does not pass the instance to and_call_original" do - klass = Class.new do + klazz = Class.new do def call(*args) args.first end end - expect_any_instance_of(klass).to receive(:call).and_call_original - instance = klass.new + expect_any_instance_of(klazz).to receive(:call).and_call_original + instance = klazz.new expect(instance.call(:bees)).to be :bees end end @@ -1159,21 +1159,21 @@ def call(*args) end it "doesn't bomb if the object doesn't support `dup`" do - klass = Class.new do + klazz = Class.new do undef_method :dup end - allow_any_instance_of(klass).to receive(:foo) + allow_any_instance_of(klazz).to receive(:foo) end it "doesn't fail when dup accepts parameters" do - klass = Class.new do + klazz = Class.new do def dup(funky_option) end end - allow_any_instance_of(klass).to receive(:foo) + allow_any_instance_of(klazz).to receive(:foo) - expect { klass.new.dup('Dup dup dup') }.to_not raise_error + expect { klazz.new.dup('Dup dup dup') }.to_not raise_error end end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index d441784a2..3a9b0cba8 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -125,35 +125,35 @@ def verify_combined_implementation end describe "a double that already has a terminal `and_return(x)` action" do - let(:dbl) { double } - let(:stubbed_double) { allow(dbl).to receive(:foo) } + let(:the_dbl) { double } + let(:stubbed_double) { allow(the_dbl).to receive(:foo) } before { stubbed_double.and_return(1) } it 'allows the terminal action to be overriden to `and_return(y)`' do stubbed_double.and_return(3) - expect(dbl.foo).to eq(3) + expect(the_dbl.foo).to eq(3) end it 'allows the terminal action to be overriden to `and_raise(y)`' do stubbed_double.and_raise("boom") - expect { dbl.foo }.to raise_error("boom") + expect { the_dbl.foo }.to raise_error("boom") end it 'allows the terminal action to be overriden to `and_throw(y)`' do stubbed_double.and_throw(:bar) - expect { dbl.foo }.to throw_symbol(:bar) + expect { the_dbl.foo }.to throw_symbol(:bar) end end describe "a double that already has a terminal block action" do - let(:dbl) { double } - let(:stubbed_double) { allow(dbl).to receive(:foo) } + let(:the_dbl) { double } + let(:stubbed_double) { allow(the_dbl).to receive(:foo) } it "allows the block action to be overridden" do allow(RSpec).to receive(:warning) stubbed_double.with(:arg) { :with_block } stubbed_double.at_least(:once) { :at_least_block } - expect(dbl.foo(:arg)).to eq(:at_least_block) + expect(the_dbl.foo(:arg)).to eq(:at_least_block) end end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index cb6bb33bb..4dac642fa 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -13,8 +13,8 @@ module Mocks end it "uses 'Double' in failure messages" do - double = double('name') - expect { double.foo }.to raise_error(/Double "name" received/) + dbl = double('name') + expect { dbl.foo }.to raise_error(/Double "name" received/) end it "hides internals in its inspect representation" do diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index b256f59a8..25e195d44 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -138,68 +138,68 @@ module Mocks end context "counts" do - let(:dbl) { double(:expected_method => nil) } + let(:the_dbl) { double(:expected_method => nil) } before do - dbl.expected_method - dbl.expected_method - dbl.expected_method + the_dbl.expected_method + the_dbl.expected_method + the_dbl.expected_method end context "exactly" do it 'passes when the message was received the given number of times' do - expect(dbl).to have_received(:expected_method).exactly(3).times + expect(the_dbl).to have_received(:expected_method).exactly(3).times end it 'fails when the message was received more times than expected' do expect { - expect(dbl).to have_received(:expected_method).exactly(1).times + expect(the_dbl).to have_received(:expected_method).exactly(1).times }.to raise_error(/expected: 1 time.*received: 3 times/m) expect { - expect(dbl).to have_received(:expected_method).exactly(2).times + expect(the_dbl).to have_received(:expected_method).exactly(2).times }.to raise_error(/expected: 2 times.*received: 3 times/m) expect { - dbl.expected_method - dbl.expected_method - expect(dbl).to have_received(:expected_method).exactly(2).times + the_dbl.expected_method + the_dbl.expected_method + expect(the_dbl).to have_received(:expected_method).exactly(2).times }.to raise_error(/expected: 2 times.*received: 5 times/m) end it 'fails when the message was received fewer times' do expect { - expect(dbl).to have_received(:expected_method).exactly(4).times + expect(the_dbl).to have_received(:expected_method).exactly(4).times }.to raise_error(/expected: 4 times.*received: 3 times/m) end end context 'at_least' do it 'passes when the message was received the given number of times' do - expect(dbl).to have_received(:expected_method).at_least(3).times + expect(the_dbl).to have_received(:expected_method).at_least(3).times end it 'passes when the message was received more times' do - expect(dbl).to have_received(:expected_method).at_least(2).times + expect(the_dbl).to have_received(:expected_method).at_least(2).times end it 'fails when the message was received fewer times' do expect { - expect(dbl).to have_received(:expected_method).at_least(4).times + expect(the_dbl).to have_received(:expected_method).at_least(4).times }.to raise_error(/expected: at least 4 times.*received: 3 times/m) end end context 'at_most' do it 'passes when the message was received the given number of times' do - expect(dbl).to have_received(:expected_method).at_most(3).times + expect(the_dbl).to have_received(:expected_method).at_most(3).times end it 'passes when the message was received fewer times' do - expect(dbl).to have_received(:expected_method).at_most(4).times + expect(the_dbl).to have_received(:expected_method).at_most(4).times end it 'fails when the message was received more times' do expect { - expect(dbl).to have_received(:expected_method).at_most(2).times + expect(the_dbl).to have_received(:expected_method).at_most(2).times }.to raise_error(/expected: at most 2 times.*received: 3 times/m) end end @@ -295,125 +295,125 @@ module Mocks end context 'ordered' do - let(:dbl) { double :one => 1, :two => 2, :three => 3 } + let(:the_dbl) { double :one => 1, :two => 2, :three => 3 } it 'passes when the messages were received in order' do - dbl.one - dbl.two + the_dbl.one + the_dbl.two - expect(dbl).to have_received(:one).ordered - expect(dbl).to have_received(:two).ordered + expect(the_dbl).to have_received(:one).ordered + expect(the_dbl).to have_received(:two).ordered end it 'passes with exact receive counts when received in order' do - 3.times { dbl.one } - 2.times { dbl.two } - dbl.three + 3.times { the_dbl.one } + 2.times { the_dbl.two } + the_dbl.three - expect(dbl).to have_received(:one).thrice.ordered - expect(dbl).to have_received(:two).twice.ordered - expect(dbl).to have_received(:three).once.ordered + expect(the_dbl).to have_received(:one).thrice.ordered + expect(the_dbl).to have_received(:two).twice.ordered + expect(the_dbl).to have_received(:three).once.ordered end pending 'passes with at most receive counts when received in order' do - dbl.one - dbl.one - dbl.two + the_dbl.one + the_dbl.one + the_dbl.two - expect(dbl).to have_received(:one).at_most(3).times.ordered - expect(dbl).to have_received(:one).at_most(:thrice).times.ordered - expect(dbl).to have_received(:two).once.ordered + expect(the_dbl).to have_received(:one).at_most(3).times.ordered + expect(the_dbl).to have_received(:one).at_most(:thrice).times.ordered + expect(the_dbl).to have_received(:two).once.ordered end pending 'passes with at least receive counts when received in order' do - dbl.one - dbl.one - dbl.two + the_dbl.one + the_dbl.one + the_dbl.two - expect(dbl).to have_received(:one).at_least(1).times.ordered - expect(dbl).to have_received(:two).once.ordered + expect(the_dbl).to have_received(:one).at_least(1).times.ordered + expect(the_dbl).to have_received(:two).once.ordered end it 'fails with exact receive counts when received out of order' do - dbl.one - dbl.two - dbl.one + the_dbl.one + the_dbl.two + the_dbl.one expect { - expect(dbl).to have_received(:one).twice.ordered - expect(dbl).to have_received(:two).once.ordered + expect(the_dbl).to have_received(:one).twice.ordered + expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) end pending "fails with at most receive counts when recieved out of order" do - dbl.one - dbl.two - dbl.one + the_dbl.one + the_dbl.two + the_dbl.one expect { - expect(dbl).to have_received(:one).at_most(2).times.ordered - expect(dbl).to have_received(:two).once.ordered + expect(the_dbl).to have_received(:one).at_most(2).times.ordered + expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) end pending "fails with at least receive counts when recieved out of order" do - dbl.one - dbl.two - dbl.one + the_dbl.one + the_dbl.two + the_dbl.one expect { - expect(dbl).to have_received(:one).at_least(1).times.ordered - expect(dbl).to have_received(:two).once.ordered + expect(the_dbl).to have_received(:one).at_least(1).times.ordered + expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) end it 'fails when the messages are received out of order' do - dbl.two - dbl.one + the_dbl.two + the_dbl.one expect { - expect(dbl).to have_received(:one).ordered - expect(dbl).to have_received(:two).ordered + expect(the_dbl).to have_received(:one).ordered + expect(the_dbl).to have_received(:two).ordered }.to raise_error(/received :two out of order/m) end context "when used with `with`" do before do - dbl.one(1) - dbl.one(2) + the_dbl.one(1) + the_dbl.one(2) end it "passes when the order lines up" do - expect(dbl).to have_received(:one).with(1).ordered - expect(dbl).to have_received(:one).with(2).ordered + expect(the_dbl).to have_received(:one).with(1).ordered + expect(the_dbl).to have_received(:one).with(2).ordered end it "fails when the order is not matched" do expect { - expect(dbl).to have_received(:one).with(2).ordered - expect(dbl).to have_received(:one).with(1).ordered + expect(the_dbl).to have_received(:one).with(2).ordered + expect(the_dbl).to have_received(:one).with(1).ordered }.to fail_with(/received :one out of order/m) end end context "when used on individually allowed messages" do before do - allow(dbl).to receive(:foo) - allow(dbl).to receive(:bar) + allow(the_dbl).to receive(:foo) + allow(the_dbl).to receive(:bar) - dbl.foo - dbl.bar + the_dbl.foo + the_dbl.bar end it 'passes when the messages were received in order' do - expect(dbl).to have_received(:foo).ordered - expect(dbl).to have_received(:bar).ordered + expect(the_dbl).to have_received(:foo).ordered + expect(the_dbl).to have_received(:bar).ordered end it 'fails when the messages are received out of order' do expect { - expect(dbl).to have_received(:bar).ordered - expect(dbl).to have_received(:foo).ordered + expect(the_dbl).to have_received(:bar).ordered + expect(the_dbl).to have_received(:foo).ordered }.to raise_error(/received :foo out of order/m) end end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 46f7d526f..527032561 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -107,17 +107,17 @@ module Mocks end it "includes the class name in the error when mocking a class method that is called an extra time with the wrong args" do - klass = Class.new do + klazz = Class.new do def self.inspect "MyClass" end end - expect(klass).to receive(:bar).with(1) - klass.bar(1) + expect(klazz).to receive(:bar).with(1) + klazz.bar(1) expect { - klass.bar(2) + klazz.bar(2) }.to raise_error(RSpec::Mocks::MockExpectationError, /MyClass/) end diff --git a/spec/rspec/mocks/space_spec.rb b/spec/rspec/mocks/space_spec.rb index c5ce5a10c..d5d8f5a16 100644 --- a/spec/rspec/mocks/space_spec.rb +++ b/spec/rspec/mocks/space_spec.rb @@ -125,8 +125,8 @@ def create_generations [:ensure_registered, :proxy_for].each do |method| describe "##{method}" do - define_method :get_proxy do |space, object| - space.__send__(method, object) + define_method :get_proxy do |the_space, object| + the_space.__send__(method, object) end it 'returns the proxy for the given object' do @@ -174,20 +174,20 @@ def create_generations describe "#constant_mutator_for" do it 'returns the mutator for the given const name' do - space = RSpec::Mocks.space + the_space = RSpec::Mocks.space stub_const("Foo", 3) stub_const("Bar", 4) - expect(space.constant_mutator_for("Foo")).to equal(space.constant_mutator_for("Foo")) - expect(space.constant_mutator_for("Bar")).to equal(space.constant_mutator_for("Bar")) - expect(space.constant_mutator_for("Foo")).not_to equal(space.constant_mutator_for("Bar")) + expect(the_space.constant_mutator_for("Foo")).to equal(the_space.constant_mutator_for("Foo")) + expect(the_space.constant_mutator_for("Bar")).to equal(the_space.constant_mutator_for("Bar")) + expect(the_space.constant_mutator_for("Foo")).not_to equal(the_space.constant_mutator_for("Bar")) end it 'can stil return a mutator from a parent context' do - space = RSpec::Mocks.space + the_space = RSpec::Mocks.space stub_const("Foo", 3) - mutator = space.constant_mutator_for("Foo") + mutator = the_space.constant_mutator_for("Foo") in_new_space_scope do subspace = RSpec::Mocks.space diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index 53cf48fff..22222bc86 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -1,45 +1,45 @@ RSpec.describe "expection set on previously stubbed method" do it "fails if message is not received after expectation is set" do - double = double(:msg => nil) - double.msg - expect(double).to receive(:msg) - expect { verify double }.to raise_error(RSpec::Mocks::MockExpectationError) + dbl = double(:msg => nil) + dbl.msg + expect(dbl).to receive(:msg) + expect { verify dbl }.to raise_error(RSpec::Mocks::MockExpectationError) end it "outputs arguments of similar calls" do - double = double('double', :foo => true) - expect(double).to receive(:foo).with('first') - double.foo('second') - double.foo('third') + dbl = double('double', :foo => true) + expect(dbl).to receive(:foo).with('first') + dbl.foo('second') + dbl.foo('third') expect { - verify double + verify dbl }.to raise_error(%Q|Double "double" received :foo with unexpected arguments\n expected: ("first")\n got: ("second"), ("third")|) - reset double + reset dbl end context "with argument constraint on stub" do it "matches any args if no arg constraint set on expectation" do - double = double("mock") - allow(double).to receive(:foo).with(3).and_return("stub") - expect(double).to receive(:foo).at_least(:once).and_return("expectation") - double.foo - verify double + dbl = double("mock") + allow(dbl).to receive(:foo).with(3).and_return("stub") + expect(dbl).to receive(:foo).at_least(:once).and_return("expectation") + dbl.foo + verify dbl end it "matches specific args set on expectation" do - double = double("mock") - allow(double).to receive(:foo).with(3).and_return("stub") - expect(double).to receive(:foo).at_least(:once).with(4).and_return("expectation") - double.foo(4) - verify double + dbl = double("mock") + allow(dbl).to receive(:foo).with(3).and_return("stub") + expect(dbl).to receive(:foo).at_least(:once).with(4).and_return("expectation") + dbl.foo(4) + verify dbl end it "fails if expectation's arg constraint is not met" do - double = double("mock") - allow(double).to receive(:foo).with(3).and_return("stub") - expect(double).to receive(:foo).at_least(:once).with(4).and_return("expectation") - double.foo(3) - expect { verify double }.to raise_error(/expected: \(4\)\s+got: \(3\)/) + dbl = double("mock") + allow(dbl).to receive(:foo).with(3).and_return("stub") + expect(dbl).to receive(:foo).at_least(:once).with(4).and_return("expectation") + dbl.foo(3) + expect { verify dbl }.to raise_error(/expected: \(4\)\s+got: \(3\)/) end it 'distinguishes between individual values and arrays properly' do diff --git a/spec/rspec/mocks/to_ary_spec.rb b/spec/rspec/mocks/to_ary_spec.rb index c6b178c3c..3a9833ae8 100644 --- a/spec/rspec/mocks/to_ary_spec.rb +++ b/spec/rspec/mocks/to_ary_spec.rb @@ -11,8 +11,8 @@ end it "supports Array#flatten" do - obj = double('foo') - expect([obj].flatten).to eq([obj]) + dbl = double('foo') + expect([dbl].flatten).to eq([dbl]) end end diff --git a/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb b/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb index 9baee520e..574d718f9 100644 --- a/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb @@ -10,13 +10,13 @@ module Mocks context "when doubling an unloaded class" do it 'allows any arguments' do expect(defined?(UnloadedClass)).to be_falsey - dbl = instance_double("UnloadedClass") + inst_dbl = instance_double("UnloadedClass") expect { - expect(dbl).to receive(:message).with(:foo, :bar, :bazz) + expect(inst_dbl).to receive(:message).with(:foo, :bar, :bazz) }.not_to raise_error - reset dbl + reset inst_dbl end end diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index 1120b99b0..5e9776166 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -154,54 +154,54 @@ module Mocks end context 'for null objects' do - let(:o) { instance_double('LoadedClass').as_null_object } + let(:obj) { instance_double('LoadedClass').as_null_object } it 'only allows defined methods' do - expect(o.defined_instance_method).to eq(o) - prevents { o.undefined_method } - prevents { o.send(:undefined_method) } - prevents { o.__send__(:undefined_method) } + expect(obj.defined_instance_method).to eq(obj) + prevents { obj.undefined_method } + prevents { obj.send(:undefined_method) } + prevents { obj.__send__(:undefined_method) } end it 'verifies arguments' do expect { - o.defined_instance_method(:too, :many, :args) + obj.defined_instance_method(:too, :many, :args) }.to raise_error(ArgumentError, "Wrong number of arguments. Expected 0, got 3.") end it "includes the double's name in a private method error" do expect { - o.rand + obj.rand }.to raise_error(NoMethodError, %r{private.*Double "LoadedClass \(instance\)"}) end it 'reports what public messages it responds to accurately' do - expect(o.respond_to?(:defined_instance_method)).to be true - expect(o.respond_to?(:defined_instance_method, true)).to be true - expect(o.respond_to?(:defined_instance_method, false)).to be true + expect(obj.respond_to?(:defined_instance_method)).to be true + expect(obj.respond_to?(:defined_instance_method, true)).to be true + expect(obj.respond_to?(:defined_instance_method, false)).to be true - expect(o.respond_to?(:undefined_method)).to be false - expect(o.respond_to?(:undefined_method, true)).to be false - expect(o.respond_to?(:undefined_method, false)).to be false + expect(obj.respond_to?(:undefined_method)).to be false + expect(obj.respond_to?(:undefined_method, true)).to be false + expect(obj.respond_to?(:undefined_method, false)).to be false end it 'reports that it responds to defined private methods when the appropriate arg is passed' do - expect(o.respond_to?(:defined_private_method)).to be false - expect(o.respond_to?(:defined_private_method, true)).to be true - expect(o.respond_to?(:defined_private_method, false)).to be false + expect(obj.respond_to?(:defined_private_method)).to be false + expect(obj.respond_to?(:defined_private_method, true)).to be true + expect(obj.respond_to?(:defined_private_method, false)).to be false end if RUBY_VERSION.to_f < 2.0 # respond_to?(:protected_method) changed behavior in Ruby 2.0. it 'reports that it responds to protected methods' do - expect(o.respond_to?(:defined_protected_method)).to be true - expect(o.respond_to?(:defined_protected_method, true)).to be true - expect(o.respond_to?(:defined_protected_method, false)).to be true + expect(obj.respond_to?(:defined_protected_method)).to be true + expect(obj.respond_to?(:defined_protected_method, true)).to be true + expect(obj.respond_to?(:defined_protected_method, false)).to be true end else it 'reports that it responds to protected methods when the appropriate arg is passed' do - expect(o.respond_to?(:defined_protected_method)).to be false - expect(o.respond_to?(:defined_protected_method, true)).to be true - expect(o.respond_to?(:defined_protected_method, false)).to be false + expect(obj.respond_to?(:defined_protected_method)).to be false + expect(obj.respond_to?(:defined_protected_method, true)).to be true + expect(obj.respond_to?(:defined_protected_method, false)).to be false end end end diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb index a91978e19..32b97b19d 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb @@ -40,16 +40,16 @@ module Mocks end context 'for null objects' do - let(:o) { instance_double('NonLoadedClass').as_null_object } + let(:obj) { instance_double('NonLoadedClass').as_null_object } it 'returns self from any message' do - expect(o.a.b.c).to be(o) + expect(obj.a.b.c).to be(obj) end it 'reports it responds to any message' do - expect(o.respond_to?(:a)).to be true - expect(o.respond_to?(:a, false)).to be true - expect(o.respond_to?(:a, true)).to be true + expect(obj.respond_to?(:a)).to be true + expect(obj.respond_to?(:a, false)).to be true + expect(obj.respond_to?(:a, true)).to be true end end From 0c50ee3fc462f33473ab0dee80954ac2c41baef0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 29 Dec 2014 15:16:37 -0800 Subject: [PATCH 0784/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 6 ++++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 85ab01e42..c97db9cef 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index a6b6fba9f..461723163 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -37,5 +37,7 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx - - rvm: 2.2 + # These two are temporary until https://github.com/travis-ci/travis-ci/issues/3067 is solved. + - rvm: jruby-18mode + - rvm: jruby fast_finish: true diff --git a/appveyor.yml b/appveyor.yml index e784692db..f61ade048 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 8ec4e6734..a5c20d550 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 848756328..db2c87644 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 5508da061..2a5e5e75b 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index dbdd5d3c6..3bc820b1b 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 8d1578e99..cc330d3ac 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From ade129ee3f6a708b8a6f1b3c153bb348d8df71af Mon Sep 17 00:00:00 2001 From: Tom Schady Date: Mon, 29 Dec 2014 17:32:06 -0600 Subject: [PATCH 0785/1179] fix spelling error --- features/working_with_legacy_code/any_instance.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/working_with_legacy_code/any_instance.feature b/features/working_with_legacy_code/any_instance.feature index 8650bfce9..1b58a2dd9 100644 --- a/features/working_with_legacy_code/any_instance.feature +++ b/features/working_with_legacy_code/any_instance.feature @@ -15,7 +15,7 @@ Feature: Any Instance discourage its use for a number of reasons: * The `rspec-mocks` API is designed for individual object instances, but this feature - operates on entire classes of objects. As a result there are some sematically confusing + operates on entire classes of objects. As a result there are some semantically confusing edge cases. For example, in `expect_any_instance_of(Widget).to receive(:name).twice` it isn't clear whether each specific instance is expected to receive `name` twice, or if two receives total are expected. (It's the former.) From 5a0a48c50810a05c63f326d56dbd759f0cc822cd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 12:54:46 -0800 Subject: [PATCH 0786/1179] Handle stubbing IO#write and then calling IO#reopen. --- Changelog.md | 4 +++ lib/rspec/mocks/method_double.rb | 27 +++++++++++++++++-- spec/rspec/mocks/partial_double_spec.rb | 36 +++++++++++++++++++++++++ tmp/.gitkeep | 0 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 tmp/.gitkeep diff --git a/Changelog.md b/Changelog.md index 257eb4019..f6f6b98ac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -45,6 +45,10 @@ Bug Fixes: (Myron Marston, #846) * Prevent message expectations from being modified by customization methods (e.g. `with`) after they have been invoked. (Sam Phippen and Melanie Gilman, #837) +* Handle cases where a method stub cannot be removed due to something + external to RSpec monkeying with the method definition. This can + happen, for example, when you `file.reopen(io)` after previously + stubbing a method on the `file` object. (Myron Marston, #853) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 34b84aafd..d2c0090f5 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -78,8 +78,7 @@ def restore_original_method return show_frozen_warning if object_singleton_class.frozen? return unless @method_is_proxied - definition_target.__send__(:remove_method, @method_name) - + remove_method_from_definition_target @method_stasher.restore if @method_stasher.method_is_stashed? restore_original_visibility @@ -255,6 +254,30 @@ def definition_target end end + + private + + def remove_method_from_definition_target + definition_target.__send__(:remove_method, @method_name) + rescue NameError + # This can happen when the method has been monkeyed with by + # something outside RSpec. This happens, for example, when + # `file.write` has been stubbed, and then `file.reopen(other_io)` + # is later called, as `File#reopen` appears to redefine `write`. + # + # Note: we could avoid rescuing this by checking + # `definition_target.instance_method(@method_name).owner == definition_target`, + # saving us from the cost of the expensive exception, but this error is + # extremely rare (it was discovered on 2014-12-30, only happens on + # RUBY_VERSION < 2.0 and our spec suite only hits this condition once), + # so we'd rather avoid the cost of that check for every method double, + # and risk the rare situation where this exception will get raised. + RSpec.warn_with( + "WARNING: RSpec could not fully restore #{@object.inspect}." \ + "#{@method_name}, possibly because the method has been redefined " \ + "by something outside of RSpec." + ) + end end end end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 527032561..bcaf53f5d 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -137,6 +137,42 @@ def self.inspect end end + RSpec.describe "Using a reopened file object as a partial mock" do + let(:file_1) { File.open(File.join("tmp", "file_1"), "w").tap { |f| f.sync = true } } + let(:file_2) { File.open(File.join("tmp", "file_2"), "w").tap { |f| f.sync = true } } + + def read_file(file) + File.open(file.path, "r", &:read) + end + + after do + file_1.close + file_2.close + end + + def expect_output_warning_on_ruby_lt_2 + if RUBY_VERSION >= '2.0' + yield + else + expect { yield }.to output(a_string_including( + "RSpec could not fully restore", file_1.inspect, "write" + )).to_stderr + end + end + + it "allows `write` to be stubbed and reset" do + allow(file_1).to receive(:write) + file_1.reopen(file_2) + expect_output_warning_on_ruby_lt_2 { reset file_1 } + + expect { + # Writing to a file that's been reopened to a 2nd file writes to both files. + file_1.write("foo") + }.to change { read_file(file_1) }.from("").to("foo"). + and change { read_file(file_2) }.from("").to("foo") + end + end + RSpec.describe "Using a partial mock on a proxy object", :if => defined?(::BasicObject) do let(:proxy_class) do Class.new(::BasicObject) do diff --git a/tmp/.gitkeep b/tmp/.gitkeep new file mode 100644 index 000000000..e69de29bb From 15637a79f6e8a6b7a08b105bce63a311b15a29fc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 31 Dec 2014 23:38:56 -0800 Subject: [PATCH 0787/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 19 ++++++++++--------- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 16 ++++++++++++---- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index c97db9cef..043dd0e95 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 461723163..dc0d119c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,28 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby sudo: false -cache: bundler +cache: + directories: + - ../rspec-core/bundle + - ../rspec-expectations/bundle + - ../rspec-mocks/bundle + - ../rspec-rails/bundle + - ../rspec-support/bundle before_install: - "script/clone_all_rspec_repos" # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it - if [ -z "$JRUBY_OPTS" ]; then gem install bundler -v=1.5.3 && alias bundle="bundle _1.5.3_"; fi -bundler_args: "--binstubs --standalone --without documentation --path ../bundle" +bundler_args: "--binstubs --standalone --without documentation --path bundle" script: "script/run_build" rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 - - 2.1.0 - - 2.1.1 - - 2.1.2 - - 2.1.3 - - 2.1.4 - - 2.1.5 + - 2.1 - 2.2 - ruby-head - ree diff --git a/appveyor.yml b/appveyor.yml index f61ade048..b746c818b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index a5c20d550..e9258f0b8 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index db2c87644..acf524547 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -11,8 +11,15 @@ SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` function clone_repo { - if [ ! -d $1 ]; then # don't clone if the dir is already there - travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" + if [ ! -e $1/Gemfile ]; then # don't reclone + # deal with our bundler cache directory. + # Git won't clone into a non-empty dir so we have to move it aside and move it back. + mkdir -p $1/bundle + pushd $1 + mv bundle ../$1-bundle + travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH ." + mv ../$1-bundle bundle + popd fi; } @@ -48,7 +55,7 @@ function run_cukes { else # Prepare RUBYOPT for scenarios that are shelling out to ruby, # and PATH for those that are using `rspec` or `rake`. - RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ + RUBYOPT="-I${PWD}/bundle -rbundler/setup" \ PATH="${PWD}/bin:$PATH" \ bin/cucumber --strict fi @@ -71,6 +78,7 @@ function run_spec_suite_for { bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done + bundle clean # prep for travis caching popd fi; } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 2a5e5e75b..50cdcdb0c 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 3bc820b1b..608eab31c 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index cc330d3ac..23afb9acc 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 61b7c1755eccf2558741683bbd8486adef3dcf3f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 1 Jan 2015 12:50:25 -0800 Subject: [PATCH 0788/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 10 +++------- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 27 ++++++++++++++------------- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 23 insertions(+), 26 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 043dd0e95..903ac50ad 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index dc0d119c4..d3dc6276e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,17 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby sudo: false cache: directories: - - ../rspec-core/bundle - - ../rspec-expectations/bundle - - ../rspec-mocks/bundle - - ../rspec-rails/bundle - - ../rspec-support/bundle + - ../bundle before_install: - "script/clone_all_rspec_repos" # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it - if [ -z "$JRUBY_OPTS" ]; then gem install bundler -v=1.5.3 && alias bundle="bundle _1.5.3_"; fi -bundler_args: "--binstubs --standalone --without documentation --path bundle" +bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: - 1.8.7 diff --git a/appveyor.yml b/appveyor.yml index b746c818b..1d99a0c87 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e9258f0b8..e410c500b 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index acf524547..aecebb0cb 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -11,15 +11,8 @@ SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` function clone_repo { - if [ ! -e $1/Gemfile ]; then # don't reclone - # deal with our bundler cache directory. - # Git won't clone into a non-empty dir so we have to move it aside and move it back. - mkdir -p $1/bundle - pushd $1 - mv bundle ../$1-bundle - travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH ." - mv ../$1-bundle bundle - popd + if [ ! -d $1 ]; then # don't clone if the dir is already there + travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" fi; } @@ -55,7 +48,7 @@ function run_cukes { else # Prepare RUBYOPT for scenarios that are shelling out to ruby, # and PATH for those that are using `rspec` or `rake`. - RUBYOPT="-I${PWD}/bundle -rbundler/setup" \ + RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ PATH="${PWD}/bin:$PATH" \ bin/cucumber --strict fi @@ -75,10 +68,18 @@ function run_spec_suite_for { echo "Running specs for $1" pushd ../$1 unset BUNDLE_GEMFILE - bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + + if [ $1 == "rspec-rails" ]; then + bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + else + # The other repos' master branch have `./bundle` as the bundler path, but we need them to all + # go into `../bundle`, so we are forcing it here rather than reading from `.travis.yml`. + # Once all the PRs are merged we can revert this. + bundle_install_flags="--binstubs --standalone --without documentation --path ../bundle" + fi + travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done - bundle clean # prep for travis caching popd fi; } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 50cdcdb0c..94bcdec7c 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 608eab31c..fb1ed5539 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 23afb9acc..4a07d6e62 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 9fe8a21c7e9e6f0dc5ceb0386f60c4fc04e767a5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 1 Jan 2015 16:46:16 -0800 Subject: [PATCH 0789/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 13 ++----------- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 18 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 903ac50ad..91d8cfb40 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index d3dc6276e..94ebd55c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 1d99a0c87..365f61c28 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e410c500b..5db111544 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index aecebb0cb..75fe0465d 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -68,16 +68,7 @@ function run_spec_suite_for { echo "Running specs for $1" pushd ../$1 unset BUNDLE_GEMFILE - - if [ $1 == "rspec-rails" ]; then - bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - else - # The other repos' master branch have `./bundle` as the bundler path, but we need them to all - # go into `../bundle`, so we are forcing it here rather than reading from `.travis.yml`. - # Once all the PRs are merged we can revert this. - bundle_install_flags="--binstubs --standalone --without documentation --path ../bundle" - fi - + bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done popd diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 94bcdec7c..a9956fc55 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index fb1ed5539..d7ac72908 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 4a07d6e62..3928cc6b6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From b0b91f3a00102f90452068b03aa2e675659bc402 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 7 Jan 2015 22:08:46 -0800 Subject: [PATCH 0790/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 5 +---- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 91d8cfb40..f7bea1c20 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 94ebd55c4..ea4f1b0fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -34,7 +34,4 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx - # These two are temporary until https://github.com/travis-ci/travis-ci/issues/3067 is solved. - - rvm: jruby-18mode - - rvm: jruby fast_finish: true diff --git a/appveyor.yml b/appveyor.yml index 365f61c28..9e4f457e3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 5db111544..f83d2e910 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 75fe0465d..a96a5c71b 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index a9956fc55..fc5d372c5 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index d7ac72908..e1edcef3a 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 3928cc6b6..77829b363 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 692f8c791869ef1e19238c724d14baa9c6a13d47 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 7 Jan 2015 23:18:58 -0800 Subject: [PATCH 0791/1179] JRuby has different behavior here. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn’t trigger the `NameError` like other rubies. --- spec/rspec/mocks/partial_double_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index bcaf53f5d..7332e8cfd 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -160,7 +160,7 @@ def expect_output_warning_on_ruby_lt_2 end end - it "allows `write` to be stubbed and reset" do + it "allows `write` to be stubbed and reset", :unless => Support::Ruby.jruby? do allow(file_1).to receive(:write) file_1.reopen(file_2) expect_output_warning_on_ruby_lt_2 { reset file_1 } From 3d29dd231469acfccc6d00cb68e1afb00ce2a7f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Jan 2015 12:11:41 -0800 Subject: [PATCH 0792/1179] Fix inaccurate doc string in cucumber scenario. This was apparently a copy/paste error. Thanks to @aruprakshit for notifying me of this problem. --- features/basics/test_doubles.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/basics/test_doubles.feature b/features/basics/test_doubles.feature index 80a1330e7..b0cc417ec 100644 --- a/features/basics/test_doubles.feature +++ b/features/basics/test_doubles.feature @@ -13,7 +13,7 @@ Feature: Test Doubles Scenario: Doubles are strict by default Given a file named "double_spec.rb" with: """ruby - RSpec.describe "double" do + RSpec.describe "A test double" do it "raises errors when messages not allowed or expected are received" do dbl = double("Some Collaborator") dbl.foo @@ -29,8 +29,8 @@ Feature: Test Doubles Scenario: A hash can be used to define allowed messages and return values Given a file named "double_spec.rb" with: """ruby - RSpec.describe "double" do - it "raises errors when messages not allowed or expected are received" do + RSpec.describe "A test double" do + it "returns canned responses from the methods named in the provided hash" do dbl = double("Some Collaborator", :foo => 3, :bar => 4) expect(dbl.foo).to eq(3) expect(dbl.bar).to eq(4) From 6cc173b537fef63d06dd5c66bbdf6cbd8ef20a5b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Jan 2015 19:12:50 -0800 Subject: [PATCH 0793/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 27 +++++++++++++-------------- script/functions.sh | 21 +++++++++++++++------ script/predicate_functions.sh | 14 +++++++++++++- script/run_build | 8 ++++---- script/travis_functions.sh | 2 +- 8 files changed, 49 insertions(+), 29 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index f7bea1c20..a8c48a397 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index ea4f1b0fc..da3b49f15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 9e4f457e3..d9bbbf668 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f83d2e910..c146d24a3 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,24 +1,23 @@ #!/bin/bash -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh -if is_mri; then - pushd .. +pushd .. - clone_repo "rspec" - clone_repo "rspec-core" - clone_repo "rspec-expectations" - clone_repo "rspec-mocks" - clone_repo "rspec-rails" +clone_repo "rspec" +clone_repo "rspec-core" +clone_repo "rspec-expectations" +clone_repo "rspec-mocks" - if rspec_support_compatible; then - clone_repo "rspec-support" - fi +if is_mri; then + clone_repo "rspec-rails" +fi - popd -else - echo "Not cloning all repos since we are not on MRI and they are only needed for the MRI build" +if rspec_support_compatible; then + clone_repo "rspec-support" fi + +popd diff --git a/script/functions.sh b/script/functions.sh index a96a5c71b..256e3855a 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -58,9 +58,13 @@ function run_cukes { function run_specs_one_by_one { echo "Running each spec file, one-by-one..." - for file in `find spec -iname '*_spec.rb'`; do - bin/rspec $file -b --format progress - done + if is_mri; then + for file in `find spec -iname '*_spec.rb'`; do + bin/rspec $file -b --format progress + done + else + echo "Skipping one-by-one specs on non-MRI rubies as they tend to have long boot times" + fi } function run_spec_suite_for { @@ -69,7 +73,7 @@ function run_spec_suite_for { pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - travis_retry eval "bundle install $bundle_install_flags" + travis_retry eval "time bundle install $bundle_install_flags" run_specs_and_record_done popd fi; @@ -121,7 +125,12 @@ function run_all_spec_suites { fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" - fold "rspec-rails specs" run_spec_suite_for "rspec-rails" + + if is_mri; then + fold "rspec-rails specs" run_spec_suite_for "rspec-rails" + else + echo "Skipping rspec-rails specs on non-MRI rubies" + fi if rspec_support_compatible; then fold "rspec-support specs" run_spec_suite_for "rspec-support" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index fc5d372c5..22ddee6a9 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -11,6 +11,18 @@ function is_mri { fi; } +function is_jruby_20_mode { + if [ -z "$JRUBY_OPTS" ]; then + if ruby -e "exit(RUBY_VERSION == '2.0.0')"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + function is_mri_192 { if is_mri; then if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then diff --git a/script/run_build b/script/run_build index e1edcef3a..e804fe9e5 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -21,8 +21,8 @@ if style_and_lint_enforced; then fold "rubocop" check_style_and_lint fi -if is_mri; then - run_all_spec_suites +if is_jruby_20_mode; then + echo "Skipping other specs suites on JRuby 2.0 mode because it is so much slower" else - echo "Skipping the rest of the build on non-MRI rubies" + run_all_spec_suites fi diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 77829b363..893d302d6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From c10548ce7e30555a23196f43fa5aa18eb1b5bfc2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 Jan 2015 16:19:50 -0800 Subject: [PATCH 0794/1179] Forwardport 2.99.3 release notes. [ci skip] --- Changelog.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index f6f6b98ac..573ba3e83 100644 --- a/Changelog.md +++ b/Changelog.md @@ -292,7 +292,7 @@ Bug Fixes: behavior. (Maurício Linhares) ### 3.0.0.beta1 / 2013-11-07 -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.2...v3.0.0.beta1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.3...v3.0.0.beta1) Breaking Changes for 3.0.0: @@ -355,6 +355,13 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.3 / 2015-01-09 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.2...v2.99.3) + +Bug Fixes: + +* Fix regression that caused an error when a test double was deserialized from YAML. (Yuji Nakayama, #777) + ### 2.99.2 / 2014-07-21 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.1...v2.99.2) From 6a7d7d815a80e5acaa90b849c8969137831ea88f Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 15 Nov 2014 17:33:56 +0800 Subject: [PATCH 0795/1179] add an api for customising how verifying doubles are checked --- lib/rspec/mocks/configuration.rb | 17 ++++++++++++++++ lib/rspec/mocks/example_methods.rb | 4 ++++ lib/rspec/mocks/object_reference.rb | 12 +++++++++++ spec/rspec/mocks/configuration_spec.rb | 14 +++++++++++++ .../verifying_doubles/construction_spec.rb | 20 +++++++++++++++++++ 5 files changed, 67 insertions(+) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index b9ee5a2bf..73d48dc34 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -104,6 +104,23 @@ def verify_doubled_constant_names? # isolated unit test will prevent you from being able to isolate it! attr_writer :verify_doubled_constant_names + # Provides a way to perform customisations when verifying doubles. + # + # @example + # + # RSpec::Mocks.configuration.when_declaring_verifying_double do |ref| + # ref.some_method! + # end + def when_declaring_verifying_double(&block) + verifying_double_declaration_callbacks << block + end + + # @api private + # Returns an array of blocks to call when verifying doubles + def verifying_double_declaration_callbacks + @verifying_double_declaration_callbacks ||= [] + end + def transfer_nested_constants? !!@transfer_nested_constants end diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index c001a95d0..c11d67548 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -410,6 +410,10 @@ def self.declare_verifying_double(type, ref, *args) "Disable check with verify_doubled_constant_names configuration option." end + RSpec::Mocks.configuration.verifying_double_declaration_callbacks.each do |block| + block.call(ref) + end + declare_double(type, ref, *args) end diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 8b4035e4f..39e1659a0 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -57,6 +57,10 @@ def const_to_replace "Can not perform constant replacement with an object." end + def const + @object.class + end + def defined? true end @@ -75,6 +79,10 @@ def const_to_replace @object.name end alias description const_to_replace + + def const + @object + end end # Used when a string is passed to `class_double`, `instance_double` @@ -97,6 +105,10 @@ def const_to_replace end alias description const_to_replace + def const + object + end + def when_loaded yield object if object end diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 724bfcf9f..3a91cdf85 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -170,6 +170,20 @@ def configure_default_syntax end end end + + describe "#when_declaring_verifying_double" do + include_context 'with isolated configuration' + + it "captures the supplied blocks" do + block = proc { |ref| ref } + block2 = proc { |ref| ref } + RSpec.configuration.mock_with :rspec do |config| + config.when_declaring_verifying_double(&block) + config.when_declaring_verifying_double(&block2) + expect(config.verifying_double_declaration_callbacks).to eq [block, block2] + end + end + end end end end diff --git a/spec/rspec/mocks/verifying_doubles/construction_spec.rb b/spec/rspec/mocks/verifying_doubles/construction_spec.rb index cb5a9b8da..2ca4f6c64 100644 --- a/spec/rspec/mocks/verifying_doubles/construction_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/construction_spec.rb @@ -3,7 +3,15 @@ module RSpec module Mocks RSpec.describe 'Constructing a verifying double' do + class ClassThatDynamicallyDefinesMethods + def self.define_attribute_methods! + define_method(:some_method_defined_dynamically) { true } + end + end + describe 'instance doubles' do + include_context 'with isolated configuration' + it 'cannot be constructed with a non-module object' do expect { instance_double(Object.new) @@ -15,6 +23,18 @@ module Mocks expect(o.defined_method).to eq(1) end + + it 'allows constants to be customised' do + test_class = Class.new(ClassThatDynamicallyDefinesMethods) + + RSpec.configuration.mock_with(:rspec) do |config| + config.when_declaring_verifying_double do |type| + type.define_attribute_methods! + end + end + + instance_double(test_class, :some_method_defined_dynamically => true) + end end describe 'class doubles' do From 2e9418bc4bff5cef5d59cac60c92d9874ad954d2 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 3 Dec 2014 11:23:38 +1100 Subject: [PATCH 0796/1179] change api call to pass reference --- spec/rspec/mocks/verifying_doubles/construction_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/verifying_doubles/construction_spec.rb b/spec/rspec/mocks/verifying_doubles/construction_spec.rb index 2ca4f6c64..6d9295d64 100644 --- a/spec/rspec/mocks/verifying_doubles/construction_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/construction_spec.rb @@ -28,8 +28,8 @@ def self.define_attribute_methods! test_class = Class.new(ClassThatDynamicallyDefinesMethods) RSpec.configuration.mock_with(:rspec) do |config| - config.when_declaring_verifying_double do |type| - type.define_attribute_methods! + config.when_declaring_verifying_double do |reference| + reference.const.define_attribute_methods! end end From 48eb2d4c06d7b9adcab660697d8ff3b890917303 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 3 Dec 2014 11:23:57 +1100 Subject: [PATCH 0797/1179] rename const to target to better reflect nature --- lib/rspec/mocks/object_reference.rb | 6 +++--- spec/rspec/mocks/verifying_doubles/construction_spec.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 39e1659a0..71e1d0246 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -57,7 +57,7 @@ def const_to_replace "Can not perform constant replacement with an object." end - def const + def target @object.class end @@ -80,7 +80,7 @@ def const_to_replace end alias description const_to_replace - def const + def target @object end end @@ -105,7 +105,7 @@ def const_to_replace end alias description const_to_replace - def const + def target object end diff --git a/spec/rspec/mocks/verifying_doubles/construction_spec.rb b/spec/rspec/mocks/verifying_doubles/construction_spec.rb index 6d9295d64..66567a3f5 100644 --- a/spec/rspec/mocks/verifying_doubles/construction_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/construction_spec.rb @@ -29,7 +29,7 @@ def self.define_attribute_methods! RSpec.configuration.mock_with(:rspec) do |config| config.when_declaring_verifying_double do |reference| - reference.const.define_attribute_methods! + reference.target.define_attribute_methods! end end From 379ada8786b00cac29ef5cba77311a41d5e545ac Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 3 Dec 2014 11:38:08 +1100 Subject: [PATCH 0798/1179] add test coverage for reference target --- .../verifying_doubles/construction_spec.rb | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/spec/rspec/mocks/verifying_doubles/construction_spec.rb b/spec/rspec/mocks/verifying_doubles/construction_spec.rb index 66567a3f5..822c5d515 100644 --- a/spec/rspec/mocks/verifying_doubles/construction_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/construction_spec.rb @@ -3,15 +3,18 @@ module RSpec module Mocks RSpec.describe 'Constructing a verifying double' do + include_context 'with isolated configuration' + class ClassThatDynamicallyDefinesMethods def self.define_attribute_methods! define_method(:some_method_defined_dynamically) { true } end end - describe 'instance doubles' do - include_context 'with isolated configuration' + module CustomModule + end + describe 'instance doubles' do it 'cannot be constructed with a non-module object' do expect { instance_double(Object.new) @@ -20,11 +23,21 @@ def self.define_attribute_methods! it 'can be constructed with a struct' do o = instance_double(Struct.new(:defined_method), :defined_method => 1) - expect(o.defined_method).to eq(1) end - it 'allows constants to be customised' do + it 'allows constants to be looked up and declared to verifying double callbacks' do + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.verify_doubled_constant_names = true + config.when_declaring_verifying_double(&probe) + end + + instance_double("RSpec::Mocks::ClassThatDynamicallyDefinesMethods") + }.to yield_with_args(have_attributes :target => ClassThatDynamicallyDefinesMethods) + end + + it 'allows classes to be customised' do test_class = Class.new(ClassThatDynamicallyDefinesMethods) RSpec.configuration.mock_with(:rspec) do |config| @@ -43,10 +56,29 @@ def self.define_attribute_methods! class_double(Object.new) }.to raise_error(/Module or String expected/) end + + it 'declares the module to verifying double callbacks' do + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.when_declaring_verifying_double(&probe) + end + class_double CustomModule + }.to yield_with_args(have_attributes :target => CustomModule) + end + end + + describe 'object doubles' do + it 'declares the class to verifying double callbacks' do + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.when_declaring_verifying_double(&probe) + end + object_double Object.new + }.to yield_with_args(have_attributes :target => Object) + end end describe 'when verify_doubled_constant_names config option is set' do - include_context "with isolated configuration" before do RSpec::Mocks.configuration.verify_doubled_constant_names = true From 79e2a877db89300858c4ccf9e75f3a2c86aed341 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 3 Dec 2014 11:48:03 +1100 Subject: [PATCH 0799/1179] update documentation for new callback --- .../verifying_doubles/dynamic_classes.feature | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index 78a754a71..8a26f4935 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -2,14 +2,17 @@ Feature: Dynamic classes Verifying instance doubles do not support methods which the class reports to not exist since an actual instance of the class would be required to verify against. This is commonly - the case when `method_missing` is used. For example, `ActiveRecord` does this to define - methods from database columns. + the case when `method_missing` is used. There are a few ways to work around this. If the object has already been loaded you may consider using an [`object_double`](./using-an-object-double), but that cannot work if you are testing in isolation. Alternatively you could implement the methods directly (calling `super` to return the `method_missing` definition). + Some of these classes may have methods to define these methods on the objects at runtime. + (For example, `ActiveRecord` does this to define methods from database columns.) For these + cases we provide an API which can be used to customise verifying doubles on creation. + These types of methods are supported at class level (with `class_double`) however, since `respond_to?` can be queried directly on the class. @@ -30,6 +33,12 @@ Feature: Dynamic classes super end end + + def self.define_attribute_methods + COLUMNS.each do |name| + define_method(name) { instance_variable_get("@#{name}") } + end + end end """ @@ -71,3 +80,30 @@ Feature: Dynamic classes When I run `rspec spec/user_spec.rb` Then the examples should all pass + + Scenario: workaround using callback + + Given a file named "lib/user.rb" with: + """ruby + require 'fake_active_record' + + class User < FakeActiveRecord + end + """ + And a file named "spec/fake_record_helper.rb" with: + """ruby + RSpec.configuration.mock_with(:rspec) do |config| + config.when_declaring_verifying_double do |reference| + reference.target.define_attribute_methods + end + end + # + # or you can use: + # + # RSpec::Mocks.configuration.when_declaring_verifying_double do |refernece| + # reference.target.define_attribute_methods + # end + """ + + When I run `rspec -r fake_record_helper spec/user_spec.rb` + Then the examples should all pass From 40f2862b8246cb77aa97153bc307f43ca8687c77 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 11 Jan 2015 11:59:52 +1100 Subject: [PATCH 0800/1179] rename DirectModuleReference to AnonymousModuleReference to accurately reflect when it is used --- lib/rspec/mocks/object_reference.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 71e1d0246..494fb5137 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -8,7 +8,7 @@ def self.for(object_module_or_name, allow_direct_object_refs=false) case object_module_or_name when Module if anonymous_module?(object_module_or_name) - DirectModuleReference.new(object_module_or_name) + AnonymousModuleReference.new(object_module_or_name) else # Use a `NamedObjectReference` if it has a name because this # will use the original value of the constant in case it has @@ -74,7 +74,7 @@ def when_loaded # Represents a reference to that module. # # @private - class DirectModuleReference < DirectObjectReference + class AnonymousModuleReference < DirectObjectReference def const_to_replace @object.name end From 774fd3f0e32f445cf53766cc59e87f57f590b311 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 10 Jan 2015 19:17:11 -0800 Subject: [PATCH 0801/1179] Make comment more accurate. --- lib/rspec/mocks/object_reference.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 494fb5137..cfa1c942f 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -70,7 +70,7 @@ def when_loaded end end - # Used when a module is passed to `class_double` or `instance_double`. + # Used when an anonymous module is passed to `class_double` or `instance_double`. # Represents a reference to that module. # # @private From 88f3f4e56f7a88e1c80ba91cfff65e2b801381a6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 10 Jan 2015 19:21:53 -0800 Subject: [PATCH 0802/1179] Add missing spec coverage for anonymous module verifying doubles. --- .../verifying_doubles/construction_spec.rb | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/verifying_doubles/construction_spec.rb b/spec/rspec/mocks/verifying_doubles/construction_spec.rb index 822c5d515..e2bb88c9e 100644 --- a/spec/rspec/mocks/verifying_doubles/construction_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/construction_spec.rb @@ -26,7 +26,7 @@ module CustomModule expect(o.defined_method).to eq(1) end - it 'allows constants to be looked up and declared to verifying double callbacks' do + it 'allows named constants to be looked up and declared to verifying double callbacks' do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.verify_doubled_constant_names = true @@ -37,6 +37,18 @@ module CustomModule }.to yield_with_args(have_attributes :target => ClassThatDynamicallyDefinesMethods) end + it 'allows anonymous constants to be looked up and declared to verifying double callbacks' do + anonymous_module = Module.new + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.verify_doubled_constant_names = true + config.when_declaring_verifying_double(&probe) + end + + instance_double(anonymous_module) + }.to yield_with_args(have_attributes :target => anonymous_module) + end + it 'allows classes to be customised' do test_class = Class.new(ClassThatDynamicallyDefinesMethods) @@ -57,7 +69,7 @@ module CustomModule }.to raise_error(/Module or String expected/) end - it 'declares the module to verifying double callbacks' do + it 'declares named modules to verifying double callbacks' do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.when_declaring_verifying_double(&probe) @@ -65,6 +77,16 @@ module CustomModule class_double CustomModule }.to yield_with_args(have_attributes :target => CustomModule) end + + it 'declares anonymous modules to verifying double callbacks' do + anonymous_module = Module.new + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.when_declaring_verifying_double(&probe) + end + class_double anonymous_module + }.to yield_with_args(have_attributes :target => anonymous_module) + end end describe 'object doubles' do From 91e6a772472b3b9c38d5da5251927c8cc094db91 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 11 Jan 2015 15:56:23 -0800 Subject: [PATCH 0803/1179] Revert "Updates from rspec-dev (2015-01-08)" --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 27 ++++++++++++++------------- script/functions.sh | 21 ++++++--------------- script/predicate_functions.sh | 14 +------------- script/run_build | 8 ++++---- script/travis_functions.sh | 2 +- 8 files changed, 29 insertions(+), 49 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index a8c48a397..f7bea1c20 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index da3b49f15..ea4f1b0fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index d9bbbf668..9e4f457e3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index c146d24a3..f83d2e910 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,23 +1,24 @@ #!/bin/bash -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh -pushd .. - -clone_repo "rspec" -clone_repo "rspec-core" -clone_repo "rspec-expectations" -clone_repo "rspec-mocks" - if is_mri; then + pushd .. + + clone_repo "rspec" + clone_repo "rspec-core" + clone_repo "rspec-expectations" + clone_repo "rspec-mocks" clone_repo "rspec-rails" -fi -if rspec_support_compatible; then - clone_repo "rspec-support" -fi + if rspec_support_compatible; then + clone_repo "rspec-support" + fi -popd + popd +else + echo "Not cloning all repos since we are not on MRI and they are only needed for the MRI build" +fi diff --git a/script/functions.sh b/script/functions.sh index 256e3855a..a96a5c71b 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -58,13 +58,9 @@ function run_cukes { function run_specs_one_by_one { echo "Running each spec file, one-by-one..." - if is_mri; then - for file in `find spec -iname '*_spec.rb'`; do - bin/rspec $file -b --format progress - done - else - echo "Skipping one-by-one specs on non-MRI rubies as they tend to have long boot times" - fi + for file in `find spec -iname '*_spec.rb'`; do + bin/rspec $file -b --format progress + done } function run_spec_suite_for { @@ -73,7 +69,7 @@ function run_spec_suite_for { pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - travis_retry eval "time bundle install $bundle_install_flags" + travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done popd fi; @@ -125,12 +121,7 @@ function run_all_spec_suites { fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" - - if is_mri; then - fold "rspec-rails specs" run_spec_suite_for "rspec-rails" - else - echo "Skipping rspec-rails specs on non-MRI rubies" - fi + fold "rspec-rails specs" run_spec_suite_for "rspec-rails" if rspec_support_compatible; then fold "rspec-support specs" run_spec_suite_for "rspec-support" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 22ddee6a9..fc5d372c5 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -11,18 +11,6 @@ function is_mri { fi; } -function is_jruby_20_mode { - if [ -z "$JRUBY_OPTS" ]; then - if ruby -e "exit(RUBY_VERSION == '2.0.0')"; then - return 0 - else - return 1 - fi - else - return 1 - fi -} - function is_mri_192 { if is_mri; then if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then diff --git a/script/run_build b/script/run_build index e804fe9e5..e1edcef3a 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -21,8 +21,8 @@ if style_and_lint_enforced; then fold "rubocop" check_style_and_lint fi -if is_jruby_20_mode; then - echo "Skipping other specs suites on JRuby 2.0 mode because it is so much slower" -else +if is_mri; then run_all_spec_suites +else + echo "Skipping the rest of the build on non-MRI rubies" fi diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 893d302d6..77829b363 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 1b264f323dc0f5a218ec93d3339d609d12058d5d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 14 Jan 2015 23:51:46 -0800 Subject: [PATCH 0804/1179] Refactor odd memoization. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is odd here because within the matcher, we call `@expectation = expect`, and within `expect` it was internally memoizing, which means `@expectation` was being assigned twice. It’s also dubious to do so because we want to minimize internal memoization since a matcher can be re-used against a different target. --- lib/rspec/mocks/matchers/have_received.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 14aeef3e9..b7611c632 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -44,7 +44,7 @@ def failure_message_when_negated end def description - expect.description_for("have received") + (@expectation ||= expect).description_for("have received") end CONSTRAINTS.each do |expectation| @@ -75,11 +75,9 @@ def disallow(type, reason="") end def expect - @expectation ||= begin - expectation = mock_proxy.build_expectation(@method_name) - apply_constraints_to expectation - expectation - end + expectation = mock_proxy.build_expectation(@method_name) + apply_constraints_to expectation + expectation end def apply_constraints_to(expectation) From da3f32216ac1f3d00e47d35212a020bfa48aab6a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 14 Jan 2015 12:58:44 +1100 Subject: [PATCH 0805/1179] docs for object references exposed by #832 callback --- lib/rspec/mocks/object_reference.rb | 58 +++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index cfa1c942f..31437e941 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -41,45 +41,70 @@ def self.anonymous_module?(mod) # Used when an object is passed to `object_double`. # Represents a reference to that object. - # - # @private class DirectObjectReference + # @param object to which this refers to. def initialize(object) @object = object end + # The object's description (via `#inspect`). + # @return String def description @object.inspect end + # Defined for interface parity with the other object reference + # implementations, it has no real relevance here. def const_to_replace raise ArgumentError, "Can not perform constant replacement with an object." end + # The target of the verifying double (the object's class). + # + # @return Class def target @object.class end + # Always returns true for an object as the class is defined. + # + # @return true def defined? true end + # Yields if the reference target is loaded, providing a generic mechanism + # to optionally run a bit of code only when a reference's target is + # loaded. + # + # This specific implementation always yields because direct references + # are always loaded. + # + # @yield object def when_loaded yield @object end end - # Used when an anonymous module is passed to `class_double` or `instance_double`. - # Represents a reference to that module. - # - # @private + # Used when an anonymous module is passed to `class_double` or + # `instance_double`. Represents a reference to that module. class AnonymousModuleReference < DirectObjectReference + # Returns the constant name to replace with a double. + # + # @return String the constant name def const_to_replace @object.name end + + # Returns the module name. + # + # @return String the constant name alias description const_to_replace + # The target of the verifying double (the module). + # + # @return Module def target @object end @@ -89,26 +114,43 @@ def target # or `object_double`. # Represents a reference to the object named (via a constant lookup) # by the string. - # - # @private class NamedObjectReference + # @param const_name [String] constant name def initialize(const_name) @const_name = const_name end + # Returns true if the named constant is defined, false otherwise. + # + # @return Boolean def defined? !!object end + # Returns the constant name to replace with a double. + # + # @return String the constant name def const_to_replace @const_name end + + # Returns the object constant name. + # + # @return String the constant name alias description const_to_replace + # The target of the verifying double (the object). + # + # @return object def target object end + # Yields if the reference target is loaded, providing a generic mechanism + # to optionally run a bit of code only when a reference's target is + # loaded. + # + # @yield object def when_loaded yield object if object end From d868ecb42a40678d0afd483937b6d8b7ff1dbdc1 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 15 Jan 2015 20:43:35 +1100 Subject: [PATCH 0806/1179] expose object rather than class as target --- lib/rspec/mocks/object_reference.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 31437e941..7e1f5465b 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -60,11 +60,11 @@ def const_to_replace "Can not perform constant replacement with an object." end - # The target of the verifying double (the object's class). + # The target of the verifying double (the object itself). # - # @return Class + # @return Object def target - @object.class + @object end # Always returns true for an object as the class is defined. From 7478671ecd12af18fb37cd0e273f7823931ad3ac Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 15 Jan 2015 15:29:00 -0800 Subject: [PATCH 0807/1179] Update spec to reflect the target being the object itself. --- spec/rspec/mocks/verifying_doubles/construction_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/verifying_doubles/construction_spec.rb b/spec/rspec/mocks/verifying_doubles/construction_spec.rb index e2bb88c9e..a40df67a8 100644 --- a/spec/rspec/mocks/verifying_doubles/construction_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/construction_spec.rb @@ -91,12 +91,14 @@ module CustomModule describe 'object doubles' do it 'declares the class to verifying double callbacks' do + object = Object.new + expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.when_declaring_verifying_double(&probe) end - object_double Object.new - }.to yield_with_args(have_attributes :target => Object) + object_double object + }.to yield_with_args(have_attributes :target => object) end end From 6493f2a09e4193ce11e2c4ce9493381a9f0b2c1b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 15 Jan 2015 15:18:01 -0800 Subject: [PATCH 0808/1179] Improve docs so they render properly with a YARD server. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we're at it, remove AnonymousModuleReference -- we don’t need it anymore! --- lib/rspec/mocks/object_reference.rb | 71 ++++++++++------------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 7e1f5465b..9bea6459d 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -8,7 +8,7 @@ def self.for(object_module_or_name, allow_direct_object_refs=false) case object_module_or_name when Module if anonymous_module?(object_module_or_name) - AnonymousModuleReference.new(object_module_or_name) + DirectObjectReference.new(object_module_or_name) else # Use a `NamedObjectReference` if it has a name because this # will use the original value of the constant in case it has @@ -39,22 +39,26 @@ def self.anonymous_module?(mod) private_class_method :anonymous_module? end - # Used when an object is passed to `object_double`. + # An implementation of rspec-mocks' reference interface. + # Used when an object is passed to {ExampleMethods#object_double}, or + # an anonymous class or module is passed to {ExampleMethods#instance_double} + # or {ExampleMethods#class_double}. # Represents a reference to that object. + # @see NamedObjectReference class DirectObjectReference - # @param object to which this refers to. + # @param object [Object] the object to which this refers def initialize(object) @object = object end - # The object's description (via `#inspect`). - # @return String + # @return [String] the object's description (via `#inspect`). def description @object.inspect end # Defined for interface parity with the other object reference - # implementations, it has no real relevance here. + # implementations. Raises an `ArgumentError` to indicate that `as_stubbed_const` + # is invalid when passing an object argument to `object_double`. def const_to_replace raise ArgumentError, "Can not perform constant replacement with an object." @@ -62,14 +66,14 @@ def const_to_replace # The target of the verifying double (the object itself). # - # @return Object + # @return [Object] def target @object end # Always returns true for an object as the class is defined. # - # @return true + # @return [true] def defined? true end @@ -81,67 +85,38 @@ def defined? # This specific implementation always yields because direct references # are always loaded. # - # @yield object + # @yield [Object] the target of this reference. def when_loaded yield @object end end - # Used when an anonymous module is passed to `class_double` or - # `instance_double`. Represents a reference to that module. - class AnonymousModuleReference < DirectObjectReference - # Returns the constant name to replace with a double. - # - # @return String the constant name - def const_to_replace - @object.name - end - - # Returns the module name. - # - # @return String the constant name - alias description const_to_replace - - # The target of the verifying double (the module). - # - # @return Module - def target - @object - end - end - - # Used when a string is passed to `class_double`, `instance_double` - # or `object_double`. + # An implementation of rspec-mocks' reference interface. + # Used when a string is passed to {ExampleMethods#object_double}, + # and when a string, named class or named module is passed to + # {ExampleMethods#instance_double}, or {ExampleMethods#class_double}. # Represents a reference to the object named (via a constant lookup) # by the string. + # @see DirectObjectReference class NamedObjectReference # @param const_name [String] constant name def initialize(const_name) @const_name = const_name end - # Returns true if the named constant is defined, false otherwise. - # - # @return Boolean + # @return [Boolean] true if the named constant is defined, false otherwise. def defined? !!object end - # Returns the constant name to replace with a double. - # - # @return String the constant name + # @return [String] the constant name to replace with a double. def const_to_replace @const_name end - - # Returns the object constant name. - # - # @return String the constant name alias description const_to_replace - # The target of the verifying double (the object). - # - # @return object + # @return [Object, nil] the target of the verifying double (the named object), or + # nil if it is not defined. def target object end @@ -150,7 +125,7 @@ def target # to optionally run a bit of code only when a reference's target is # loaded. # - # @yield object + # @yield [Object] the target object def when_loaded yield object if object end From 152cc5ef65fa02563b52f2243689f2d5e804fa17 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 15 Jan 2015 15:22:38 -0800 Subject: [PATCH 0809/1179] Clarify error message. --- lib/rspec/mocks/object_reference.rb | 2 +- spec/rspec/mocks/verifying_doubles/object_double_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 9bea6459d..57ef71179 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -61,7 +61,7 @@ def description # is invalid when passing an object argument to `object_double`. def const_to_replace raise ArgumentError, - "Can not perform constant replacement with an object." + "Can not perform constant replacement with an anonymous object." end # The target of the verifying double (the object itself). diff --git a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb index e32d844e8..9694e75db 100644 --- a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb @@ -50,7 +50,7 @@ module Mocks it 'does not allow as_stubbed_constant for real objects' do expect { object_double(LoadedClass.new).as_stubbed_const - }.to raise_error(/Can not perform constant replacement with an object/) + }.to raise_error(/Can not perform constant replacement with an anonymous object/) end it 'is not a module' do From ed3fb3b1ac9e1187eeb44f4a68dd9878f9ce3ec6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 14 Jan 2015 23:44:41 -0800 Subject: [PATCH 0810/1179] Provide a clear error when received message args are mutated. ...before a `have_received(...).with(...)` expectation. Fixes #865. --- Changelog.md | 2 + lib/rspec/mocks/error_generator.rb | 4 ++ lib/rspec/mocks/message_expectation.rb | 11 +++++ lib/rspec/mocks/proxy.rb | 43 +++++++++++++++++-- .../mocks/matchers/have_received_spec.rb | 28 ++++++++++++ 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 573ba3e83..80db09106 100644 --- a/Changelog.md +++ b/Changelog.md @@ -49,6 +49,8 @@ Bug Fixes: external to RSpec monkeying with the method definition. This can happen, for example, when you `file.reopen(io)` after previously stubbing a method on the `file` object. (Myron Marston, #853) +* Provide a clear error when received message args are mutated before + a `have_received(...).with(...)` expectation. (Myron Marston, #868) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 3afbf688a..110a8f898 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -15,6 +15,10 @@ module Mocks # invoked. MockExpectationAlreadyInvokedError = Class.new(Exception) + # Raised for situations that RSpec cannot support due to external mutations + # made externally on arguments that RSpec is holding onto to use for later comparisons. + CannotSupportArgMutationsError = Class.new(StandardError) + # @private UnsupportedMatcherError = Class.new(StandardError) # @private diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 43d8479b9..532b26d3b 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -510,6 +510,17 @@ def increase_actual_received_count! @actual_received_count += 1 end + def fail_if_problematic_received_arg_mutations(received_arg_list) + return if @argument_list_matcher == ArgumentListMatcher::MATCH_ALL + return unless received_arg_list.has_mutations? + + raise CannotSupportArgMutationsError, + "`have_received(...).with(...)` cannot be used when received " \ + "message args have later been mutated. You can use a normal " \ + "message expectation (`expect(...).to receive(...).with(...)`) " \ + "instead." + end + private def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index b936a3a62..420374f68 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -88,7 +88,9 @@ def replay_received_message_on(expectation, &block) @error_generator.raise_expectation_on_unstubbed_method(expected_method_name) end - @messages_received.each do |(actual_method_name, args, _)| + @messages_received.each do |(actual_method_name, received_arg_list, _)| + expectation.fail_if_problematic_received_arg_mutations(received_arg_list) + args = received_arg_list.args next unless expectation.matches?(actual_method_name, *args) expectation.safe_invoke(nil) @@ -98,7 +100,8 @@ def replay_received_message_on(expectation, &block) # @private def check_for_unexpected_arguments(expectation) - @messages_received.each do |(method_name, args, _)| + @messages_received.each do |(method_name, received_arg_list, _)| + args = received_arg_list.args next unless expectation.matches_name_but_not_args(method_name, *args) raise_unexpected_message_args_error(expectation, *args) @@ -138,7 +141,11 @@ def reset # @private def received_message?(method_name, *args, &block) - @messages_received.any? { |array| array == [method_name, args, block] } + @messages_received.any? do |(received_method_name, received_arg_list, received_block)| + method_name == received_method_name && + args == received_arg_list.args && + block == received_block + end end # @private @@ -149,7 +156,35 @@ def has_negative_expectation?(message) # @private def record_message_received(message, *args, &block) @order_group.invoked SpecificMessage.new(object, message, args) - @messages_received << [message, args, block] + @messages_received << [message, ReceivedArgList.new(args), block] + end + + class ReceivedArgList + attr_reader :args + + def initialize(args) + @args = args + @original_hashes = args.map { |arg| [arg, hash_of(arg)] } + end + + def has_mutations? + @original_hashes.any? { |(arg, hash)| hash != hash_of(arg) } + end + + private + + def hash_of(arg) + arg.hash + rescue Exception + # While `Object#hash` is a built-in ruby method that we expect args to + # support, there's no guarantee that all args will. For example, a + # `BasicObject` instance will raise a `NoMethodError`. Given that + # we use the hash only to advise the user of a rare case we don't + # support involving mutations, it seems better to ignore this error + # and use a static value in its place (which will make us assume no + # mutation has occurred). + :failed_to_get_hash + end end # @private diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 25e195d44..8ee8180fd 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -123,6 +123,34 @@ module Mocks expect(dbl).to have_received(:expected_method).with(:unexpected, :args) }.to raise_error(/with unexpected arguments/) end + + context "when a passed arg is later mutated before the `have_received(...).with(...)` expectation" do + it 'gives the user a clear error since we cannot support mutations' do + dbl = spy + dbl.foo(arg = "bar") + arg << "bazz" + + expect { + expect(dbl).to have_received(:foo).with("bar") + }.to raise_error(CannotSupportArgMutationsError) + end + + it 'does not prevent `have_received` without `with`' do + dbl = spy + dbl.foo(arg = "bar") + arg << "bazz" + + expect(dbl).to have_received(:foo) + end + end + + it 'does not blow up when objects that do not support `#hash` are passed as args', :if => defined?(::BasicObject) do + dbl = spy + dbl.foo(arg = ::BasicObject.new) + expect { arg.hash }.to raise_error(NoMethodError) + + expect(dbl).to have_received(:foo).with(arg) + end end it 'generates a useful description' do From 52c69f1ef8ac2c0de5ec08c72df709593a6a8ba5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 16 Jan 2015 08:13:51 -0800 Subject: [PATCH 0811/1179] Just use `Array#hash`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As @jferris pointed out, it’s sufficient. Besides being simpler (and likely faster, though I haven’t benchmarked it), this does fewer object allocations. --- lib/rspec/mocks/proxy.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 420374f68..245101d27 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -163,12 +163,12 @@ class ReceivedArgList attr_reader :args def initialize(args) - @args = args - @original_hashes = args.map { |arg| [arg, hash_of(arg)] } + @args = args + @original_hash = hash_of(args) end def has_mutations? - @original_hashes.any? { |(arg, hash)| hash != hash_of(arg) } + @original_hash != hash_of(args) end private From 97ba3029060810753f2cad579a19b0b040183a12 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Fri, 16 Jan 2015 19:46:45 +0000 Subject: [PATCH 0812/1179] Make and_wrap_original work with any_instance (fix #867) There were a couple of different issues preventing `and_wrap_original` from working when used with `any_instance`. --- lib/rspec/mocks/any_instance/chain.rb | 1 + lib/rspec/mocks/any_instance/stub_chain.rb | 3 +- spec/rspec/mocks/and_wrap_original_spec.rb | 94 +++++++++++++--------- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index ebc3e4f9e..db9d6184b 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -34,6 +34,7 @@ def self.record(method_name) record :and_throw record :and_yield record :and_call_original + record :and_wrap_original record :with record :once record :twice diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index c4fa2e295..adc4e6bb4 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -31,7 +31,8 @@ def invocation_order :and_return => [:with, nil], :and_raise => [:with, nil], :and_yield => [:with, nil], - :and_call_original => [:with, nil] + :and_call_original => [:with, nil], + :and_wrap_original => [:with, nil] } end diff --git a/spec/rspec/mocks/and_wrap_original_spec.rb b/spec/rspec/mocks/and_wrap_original_spec.rb index e66003c3e..cf6d96ece 100644 --- a/spec/rspec/mocks/and_wrap_original_spec.rb +++ b/spec/rspec/mocks/and_wrap_original_spec.rb @@ -10,54 +10,68 @@ def results let(:instance) { klass.new } - it "allows us to modify the results of the original method" do - expect { - allow(instance).to receive(:results).and_wrap_original do |m| - m.call.first(10) - end - }.to change { instance.results.size }.from(100).to(10) - end + shared_examples "using and_wrap_original" do + it "allows us to modify the results of the original method" do + expect { + allow_it.to receive(:results).and_wrap_original do |m| + m.call.first(10) + end + }.to change { instance.results.size }.from(100).to(10) + end - it "raises a name error if the method does not exist" do - expect { - allow(instance).to receive(:no_results).and_wrap_original { |m| m.call } - instance.no_results - }.to raise_error NameError - end + it "raises a name error if the method does not exist" do + expect { + allow_it.to receive(:no_results).and_wrap_original { |m| m.call } + instance.no_results + }.to raise_error NameError + end - it "passes in the original method" do - value = nil - original_method = instance.method(:results) - allow(instance).to receive(:results).and_wrap_original { |m| value = m } - instance.results - expect(value).to eq original_method - end + it "passes in the original method" do + value = nil + original_method = instance.method(:results) + allow_it.to receive(:results).and_wrap_original { |m| value = m } + instance.results + expect(value).to eq original_method + end - it "passes along the message arguments" do - values = nil - allow(instance).to receive(:results).and_wrap_original { |_, *args| values = args } - instance.results(1, 2, 3) - expect(values).to eq [1, 2, 3] - end + it "passes along the message arguments" do + values = nil + allow_it.to receive(:results).and_wrap_original { |_, *args| values = args } + instance.results(1, 2, 3) + expect(values).to eq [1, 2, 3] + end + + it "passes along any supplied block" do + value = nil + allow_it.to receive(:results).and_wrap_original { |&b| value = b } + instance.results(&(block = proc {})) + expect(value).to eq block + end + + it "ignores previous stubs" do + allow_it.to receive(:results) { "results" } + allow_it.to receive(:results).and_wrap_original { |m| m.call } + expect(instance.results).to_not eq "results" + end - it "passes along any supplied block" do - value = nil - allow(instance).to receive(:results).and_wrap_original { |&b| value = b } - instance.results(&(block = proc {})) - expect(value).to eq block + it "can be constrained by specific arguments" do + allow_it.to receive(:results) { :all } + allow_it.to receive(:results).with(5).and_wrap_original { |m, n| m.call.first(n) } + expect(instance.results 5).to eq [1,2,3,4,5] + expect(instance.results).to eq :all + end end - it "ignores previous stubs" do - allow(instance).to receive(:results) { "results" } - allow(instance).to receive(:results).and_wrap_original { |m| m.call } - expect(instance.results).to_not eq "results" + describe "allow(...).to receive(...).and_wrap_original" do + it_behaves_like "using and_wrap_original" do + let(:allow_it) { allow(instance) } + end end - it "can be constrained by specific arguments" do - allow(instance).to receive(:results) { :all } - allow(instance).to receive(:results).with(5).and_wrap_original { |m, n| m.call.first(n) } - expect(instance.results 5).to eq [1,2,3,4,5] - expect(instance.results).to eq :all + describe "allow_any_instance_of(...).to receive(...).and_wrap_original" do + it_behaves_like "using and_wrap_original" do + let(:allow_it) { allow_any_instance_of(klass) } + end end end end From 65d3b6534292e286dd2883098508cfa881164e72 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 16 Jan 2015 21:34:10 -0800 Subject: [PATCH 0813/1179] Add changelog for #869. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 573ba3e83..d338e91b0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,8 @@ Enhancements: Baginski, #826) * Make `array_including` and `hash_including` argument matchers composable. (Sam Phippen, #819) +* Make `allow_any_instance_of(...).to receive(...).and_wrap_original` + work. (Ryan Fitzgerald, #869) Bug Fixes: From d65446b0397af8d879f7fbafdeb0a72b517c484a Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Sat, 17 Jan 2015 15:28:01 -0500 Subject: [PATCH 0814/1179] Include details on how to run against `master`. A very common issue is when a user attempts to run against the `master` branch. Simply pointing to `master` doesn't work. This adds the steps necessary to use `master`. We need all of the repos in order to also satisfy any other gems which may have a dependency on the `rspec` gem. [ci skip] --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 325b2ae04..4175ff854 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,15 @@ alike. gem install rspec # for rspec-core, rspec-expectations, rspec-mocks gem install rspec-mocks # for rspec-mocks only +Want to run against the `master` branch? You'll need to include the dependent +RSpec repos as well. Add the following to your `Gemfile`: + +```ruby +%w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| + gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => 'master' +end +``` + ## Test Doubles A test double is an object that stands in for another object in your system From 25d4345ec05eedd4940b3d6bf4989ad882ffe0d5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 14 Jan 2015 13:07:14 +1100 Subject: [PATCH 0815/1179] update docs for rspec/rspec_rails#1238 [skip ci] --- features/verifying_doubles/dynamic_classes.feature | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index 8a26f4935..e7b52fc9f 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -11,7 +11,8 @@ Feature: Dynamic classes Some of these classes may have methods to define these methods on the objects at runtime. (For example, `ActiveRecord` does this to define methods from database columns.) For these - cases we provide an API which can be used to customise verifying doubles on creation. + cases we provide an API which can be used to customise verifying doubles on creation. We + use this ourselves in `rspec-rails` to set up some niceties for you. These types of methods are supported at class level (with `class_double`) however, since `respond_to?` can be queried directly on the class. From 8a0962a6b8a896b1f1f2806a1282f29cde7d2baa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 18 Jan 2015 20:54:51 -0800 Subject: [PATCH 0816/1179] =?UTF-8?q?Reword=20comment=20to=20not=20use=20?= =?UTF-8?q?=E2=80=9Cexternal=E2=80=9D=20twice.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to @waterlink for pointing this out. --- lib/rspec/mocks/error_generator.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 110a8f898..d18995022 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -15,8 +15,9 @@ module Mocks # invoked. MockExpectationAlreadyInvokedError = Class.new(Exception) - # Raised for situations that RSpec cannot support due to external mutations - # made externally on arguments that RSpec is holding onto to use for later comparisons. + # Raised for situations that RSpec cannot support due to mutations made + # externally on arguments that RSpec is holding onto to use for later + # comparisons. CannotSupportArgMutationsError = Class.new(StandardError) # @private From fc9e9a59904ec52fa8e26e07ee65c41029ef18d0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 18 Jan 2015 20:58:27 -0800 Subject: [PATCH 0817/1179] Ignore arg mutations for method calls we are not concerned with. --- lib/rspec/mocks/proxy.rb | 5 ++++- spec/rspec/mocks/matchers/have_received_spec.rb | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 245101d27..ca9a12e99 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -89,7 +89,10 @@ def replay_received_message_on(expectation, &block) end @messages_received.each do |(actual_method_name, received_arg_list, _)| - expectation.fail_if_problematic_received_arg_mutations(received_arg_list) + if expectation.message == actual_method_name + expectation.fail_if_problematic_received_arg_mutations(received_arg_list) + end + args = received_arg_list.args next unless expectation.matches?(actual_method_name, *args) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 8ee8180fd..8e51b0c1b 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -142,6 +142,15 @@ module Mocks expect(dbl).to have_received(:foo) end + + it 'does not prevent `have_received(...).with(...)` for a different method that has had no arg mutations' do + dbl = spy + dbl.foo(arg = "foo") + arg << "foo" + dbl.bar("bar") + + expect(dbl).to have_received(:bar).with("bar") + end end it 'does not blow up when objects that do not support `#hash` are passed as args', :if => defined?(::BasicObject) do From 73ac1f6d44e181b25603e782a225f311706a66a9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 21 Jan 2015 00:48:51 -0800 Subject: [PATCH 0818/1179] Better formatting for error message. --- lib/rspec/mocks/example_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index c11d67548..0ee49be71 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -405,9 +405,9 @@ def self.declare_verifying_double(type, ref, *args) !ref.defined? raise VerifyingDoubleNotDefinedError, - "#{ref.description} is not a defined constant. " \ + "#{ref.description.inspect} is not a defined constant. " \ "Perhaps you misspelt it? " \ - "Disable check with verify_doubled_constant_names configuration option." + "Disable check with `verify_doubled_constant_names` configuration option." end RSpec::Mocks.configuration.verifying_double_declaration_callbacks.each do |block| From 235e43ef2e3d365ccd035a1fd4a973f29360308a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 21 Jan 2015 00:50:36 -0800 Subject: [PATCH 0819/1179] Deal with verified doubles for classes that redefined `name`. These sorts of classes worked fine before #824 was merged, and the switch to using a NamedObjectReference based on the name broke things for them. This restores support. --- lib/rspec/mocks/object_reference.rb | 14 +++++++++++--- .../class_double_with_class_loaded_spec.rb | 16 ++++++++++++++++ .../instance_double_with_class_loaded_spec.rb | 8 ++++++++ spec/support/doubled_classes.rb | 6 ++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/object_reference.rb b/lib/rspec/mocks/object_reference.rb index 57ef71179..cce2c3313 100644 --- a/lib/rspec/mocks/object_reference.rb +++ b/lib/rspec/mocks/object_reference.rb @@ -13,7 +13,7 @@ def self.for(object_module_or_name, allow_direct_object_refs=false) # Use a `NamedObjectReference` if it has a name because this # will use the original value of the constant in case it has # been stubbed. - NamedObjectReference.new(object_module_or_name.name) + NamedObjectReference.new(name_of(object_module_or_name)) end when String NamedObjectReference.new(object_module_or_name) @@ -29,14 +29,22 @@ def self.for(object_module_or_name, allow_direct_object_refs=false) if Module.new.name.nil? def self.anonymous_module?(mod) - !mod.name + !name_of(mod) end else # 1.8.7 def self.anonymous_module?(mod) - mod.name == "" + name_of(mod) == "" end end private_class_method :anonymous_module? + + def self.name_of(mod) + MODULE_NAME_METHOD.bind(mod).call + end + private_class_method :name_of + + # @private + MODULE_NAME_METHOD = Module.instance_method(:name) end # An implementation of rspec-mocks' reference interface. diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index cfac28729..4ecd87976 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -124,6 +124,22 @@ module Mocks end end + context "when given a class that has an overriden `#name` method" do + it "properly verifies" do + check_verification class_double(LoadedClassWithOverridenName) + end + + it "can still stub the const" do + class_double(LoadedClassWithOverridenName).as_stubbed_const + check_verification LoadedClassWithOverridenName + end + + def check_verification(o) + allow(o).to receive(:defined_class_method) + prevents { allow(o).to receive(:undefined_method) } + end + end + context "when the class const has been previously stubbed" do before { stub_const("LoadedClass", Class.new) } diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index 5e9776166..c0b1bb31a 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -153,6 +153,14 @@ module Mocks end end + context "when given a class that has an overriden `#name` method" do + it "properly verifies" do + o = instance_double(LoadedClassWithOverridenName) + allow(o).to receive(:defined_instance_method) + prevents { allow(o).to receive(:undefined_method) } + end + end + context 'for null objects' do let(:obj) { instance_double('LoadedClass').as_null_object } diff --git a/spec/support/doubled_classes.rb b/spec/support/doubled_classes.rb index 3405b1a5c..c83e9afdb 100644 --- a/spec/support/doubled_classes.rb +++ b/spec/support/doubled_classes.rb @@ -78,3 +78,9 @@ def defined_private_method "wink wink ;)" end end + +class LoadedClassWithOverridenName < LoadedClass + def self.name + "Overriding name is not a good idea but we can't count on users not doing this" + end +end From 3ab98c5c005432d56c1dd40952accff9ab577de6 Mon Sep 17 00:00:00 2001 From: Siva Gollapalli Date: Thu, 22 Jan 2015 23:04:55 +0530 Subject: [PATCH 0820/1179] FIX# Displaying 'Diff' in new line --- lib/rspec/mocks/error_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index d18995022..08018d246 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -65,7 +65,7 @@ def raise_missing_default_stub_error(expectation, *args) diff = diff_message(expectation.expected_args, args) message = default_error_message(expectation, expected_args, actual_args) - message << "Diff:\n #{diff}" unless diff.empty? + message << "\n Diff:\n #{diff}" unless diff.empty? message << "\n Please stub a default value first if message might be received with other args as well. \n" __raise message From b8a838ad17875ac0ec5706dc5f721069304e3609 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 27 Jan 2015 07:45:50 -0800 Subject: [PATCH 0821/1179] Improve explanation about verified double argument verification. Closes #878. --- features/verifying_doubles/instance_doubles.feature | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/features/verifying_doubles/instance_doubles.feature b/features/verifying_doubles/instance_doubles.feature index e21948c92..c234ca0e5 100644 --- a/features/verifying_doubles/instance_doubles.feature +++ b/features/verifying_doubles/instance_doubles.feature @@ -2,8 +2,10 @@ Feature: Using an instance double An `instance_double` is the most common type of verifying double. It takes a class name or object as its first argument, then verifies that any methods being stubbed would be present - on an _instance_ of that class. If any argument matchers are specified, it also verifies that the - number of arguments is correct. + on an _instance_ of that class. In addition, when it receives messages, it verifies that the + provided arguments are supported by the method signature, both in terms of arity and + allowed or required keyword arguments, if any. The same argument verification happens + when you [constrain the arguments](../setting-constraints/matching-arguments) using `with`. For methods handled by `method_missing`, see [dynamic classes](./dynamic-classes). From 9edc01779be6100660ab36ff2253bb07af21fd60 Mon Sep 17 00:00:00 2001 From: Siva Gollapalli Date: Wed, 28 Jan 2015 22:48:57 +0530 Subject: [PATCH 0822/1179] Removed extra space to make left align --- lib/rspec/mocks/error_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 08018d246..04b2c4590 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -65,7 +65,7 @@ def raise_missing_default_stub_error(expectation, *args) diff = diff_message(expectation.expected_args, args) message = default_error_message(expectation, expected_args, actual_args) - message << "\n Diff:\n #{diff}" unless diff.empty? + message << "\nDiff:\n #{diff}" unless diff.empty? message << "\n Please stub a default value first if message might be received with other args as well. \n" __raise message From 6fc4603ee07d59e869792388a30bff6f67d00723 Mon Sep 17 00:00:00 2001 From: Siva Gollapalli Date: Wed, 28 Jan 2015 23:10:43 +0530 Subject: [PATCH 0823/1179] Added specs for #875 --- spec/rspec/mocks/error_generator_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/rspec/mocks/error_generator_spec.rb b/spec/rspec/mocks/error_generator_spec.rb index 92aa0b931..07c712561 100644 --- a/spec/rspec/mocks/error_generator_spec.rb +++ b/spec/rspec/mocks/error_generator_spec.rb @@ -44,6 +44,14 @@ def self.name }.to fail_with(unexpected_failure_message_for(o.inspect)) end end + + context "on default method stub" do + it "error message display starts in new line" do + d = double(:double) + allow(d).to receive(:foo).with({}) + expect { d.foo([]) }.to fail_with(/\nDiff/) + end + end end end end From ab93dda96da364c8fa6d0b307d7b3049f2d24cb6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 29 Jan 2015 10:12:24 -0800 Subject: [PATCH 0824/1179] Explicitly tag pending specs for ordered/vague count combinations. This will now print the reason it is pending on CI and skip them locally since it adds a lot of noise to our output and we have yet to attempt to address them. --- spec/rspec/mocks/matchers/have_received_spec.rb | 8 ++++---- spec/rspec/mocks/matchers/receive_spec.rb | 4 ++-- spec/spec_helper.rb | 9 +++++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 8e51b0c1b..1d23b97fc 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -352,7 +352,7 @@ module Mocks expect(the_dbl).to have_received(:three).once.ordered end - pending 'passes with at most receive counts when received in order' do + it 'passes with at most receive counts when received in order', :ordered_and_vauge_counts_unsupported do the_dbl.one the_dbl.one the_dbl.two @@ -362,7 +362,7 @@ module Mocks expect(the_dbl).to have_received(:two).once.ordered end - pending 'passes with at least receive counts when received in order' do + it 'passes with at least receive counts when received in order', :ordered_and_vauge_counts_unsupported do the_dbl.one the_dbl.one the_dbl.two @@ -382,7 +382,7 @@ module Mocks }.to raise_error(/received :two out of order/m) end - pending "fails with at most receive counts when recieved out of order" do + it "fails with at most receive counts when recieved out of order", :ordered_and_vauge_counts_unsupported do the_dbl.one the_dbl.two the_dbl.one @@ -393,7 +393,7 @@ module Mocks }.to raise_error(/received :two out of order/m) end - pending "fails with at least receive counts when recieved out of order" do + it "fails with at least receive counts when recieved out of order", :ordered_and_vauge_counts_unsupported do the_dbl.one the_dbl.two the_dbl.one diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index e53008e53..a6c205862 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -344,7 +344,7 @@ def receiver.method_missing(*a); end # a poor man's stub... dbl.two end - pending "fails with at least when the ordering is incorrect" do + it "fails with at least when the ordering is incorrect", :ordered_and_vauge_counts_unsupported do expect { expect(dbl).to receive(:one).at_least(2).times.ordered expect(dbl).to receive(:two).once.ordered @@ -364,7 +364,7 @@ def receiver.method_missing(*a); end # a poor man's stub... dbl.two end - pending "fails with at most when the ordering is incorrect" do + it "fails with at most when the ordering is incorrect", :ordered_and_vauge_counts_unsupported do expect { expect(dbl).to receive(:one).at_most(2).times.ordered expect(dbl).to receive(:two).once.ordered diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c461eed77..2b4f0d6a0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -104,6 +104,15 @@ def self.fake_matcher_description config.extend RSpec::Support::RubyFeatures config.include RSpec::Support::RubyFeatures + config.define_derived_metadata :ordered_and_vauge_counts_unsupported do |meta| + meta[:pending] = "`.ordered` combined with a vauge count (e.g. `at_least` or `at_most`) is not yet supported (see #713)" + end + + # We have yet to try to address this issue, and it's just noise in our output, + # so skip it locally. However, on CI we want it to still run them so that if + # we do something that makes these specs pass, we are notified. + config.filter_run_excluding :ordered_and_vauge_counts_unsupported unless ENV['CI'] + RSpec::Matchers.define_negated_matcher :a_string_excluding, :include end From ac486cd90c3cf2e99c4689a30f5a077030c58fdf Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Sat, 31 Jan 2015 13:45:38 +0100 Subject: [PATCH 0825/1179] Removed rubysl from the Gemfile. This hasn't been required anymore since quite a while. Starting with 2.2.0 (if I remember correctly) Rubinius takes care of ensuring the rubysl Gems are always available, with or without Bundler. --- Gemfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Gemfile b/Gemfile index 3bd498c10..fcccdb837 100644 --- a/Gemfile +++ b/Gemfile @@ -27,8 +27,4 @@ platforms :jruby do gem "jruby-openssl" end -platforms :rbx do - gem 'rubysl' -end - eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') From 90cb4718b47b5663983f799444bd61c945ce7cd2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 3 Feb 2015 07:28:35 -0800 Subject: [PATCH 0826/1179] Update changelog for v3.2.0 [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index d3851fefc..33bb8abbd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...master) +### 3.2.0 / 2015-02-03 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) Enhancements: From d7ed065247a2968d90a898f3be659e39a96decf7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 3 Feb 2015 07:31:44 -0800 Subject: [PATCH 0827/1179] Release 3.2.0 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index f18e4652e..bd7362710 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.2.0.pre' + STRING = '3.2.0' end end end From 44d81d62d1e478781b6b35b1b3c0336310382edf Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 3 Feb 2015 08:08:32 -0800 Subject: [PATCH 0828/1179] Bump version to 3.3.0.pre --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index bd7362710..4d95c39ea 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.2.0' + STRING = '3.3.0.pre' end end end From 1184e786ba85881d39b3f167d6b6c28992c36fb1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 3 Feb 2015 22:13:58 -0800 Subject: [PATCH 0829/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 4 ++-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index f7bea1c20..3732f5298 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index ea4f1b0fc..91d9e150c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 9e4f457e3..55ad89c7d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -29,6 +29,6 @@ test_script: environment: matrix: - # ruby_version: '20' doesn't work for some reason + - ruby_version: '200' - ruby_version: '193' - ruby_version: '21' diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f83d2e910..7ba918090 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index a96a5c71b..ac8bfa394 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index fc5d372c5..88f2c0f24 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index e1edcef3a..2d081e216 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 77829b363..a2c393362 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 990b0176d724844af8f6cedfd3b76f1a766db23b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 3 Feb 2015 23:51:21 -0800 Subject: [PATCH 0830/1179] When stubbing `MyClass.new` verify against `MyClass#initialize`. Closes #883. --- Changelog.md | 9 ++++ lib/rspec/mocks/method_reference.rb | 37 ++++++++++++++ lib/rspec/mocks/verifying_proxy.rb | 23 ++++++++- spec/rspec/mocks/partial_double_spec.rb | 49 ++++++++++++++++++- .../class_double_with_class_loaded_spec.rb | 49 +++++++++++++++++++ .../verifying_doubles/object_double_spec.rb | 8 +-- spec/support/doubled_classes.rb | 3 ++ 7 files changed, 172 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 33bb8abbd..ba3ebc8d7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 3.3.0 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...master) + +Enhancements: + +* When stubbing `new` on `MyClass` or `class_double(MyClass)`, use the + method signature from `MyClass#initialize` to verify arguments. + (Myron Marston, #886) + ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 21896bf07..513706e37 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -6,6 +6,10 @@ module Mocks # # @private class MethodReference + def self.for(object_reference, method_name) + new(object_reference, method_name) + end + def initialize(object_reference, method_name) @object_reference = object_reference @method_name = method_name @@ -133,6 +137,14 @@ def visibility_from(mod) # @private class ObjectMethodReference < MethodReference + def self.for(object_reference, method_name) + if ClassNewMethodReference.applies_to?(method_name) { object_reference.when_loaded { |o| o } } + ClassNewMethodReference.new(object_reference, method_name) + else + super + end + end + private def method_implemented?(object) @@ -151,5 +163,30 @@ def visibility_from(object) MethodReference.method_visibility_for(object, @method_name) end end + + # When a class's `.new` method is stubbed, we want to use the method + # signature from `#initialize` because `.new`'s signature is a generic + # `def new(*args)` and it simply delegates to `#initialize` and forwards + # all args...so the method with the actually used signature is `#initialize`. + # + # This method reference implementation handles that specific case. + # @private + class ClassNewMethodReference < ObjectMethodReference + def self.applies_to?(method_name) + return false unless method_name == :new + klass = yield + return false unless klass.respond_to?(:new, true) + + # We only want to apply our special logic to normal `new` methods. + # Methods that the user has monkeyed with should be left as-is. + klass.method(:new).owner == ::Class + end + + def with_signature + @object_reference.when_loaded do |klass| + yield Support::MethodSignature.new(klass.instance_method(:initialize)) + end + end + end end end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 84ed3c63a..57f2df417 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -71,7 +71,7 @@ def initialize(object, order_group, name, doubled_module, method_reference_class def method_reference @method_reference ||= Hash.new do |h, k| - h[k] = @method_reference_class.new(@doubled_module, k) + h[k] = @method_reference_class.for(@doubled_module, k) end end @@ -95,7 +95,7 @@ def initialize(object, expectation_ordering) # A custom method double is required to pass through a way to lookup # methods to determine their parameters. @method_doubles = Hash.new do |h, k| - h[k] = VerifyingExistingMethodDouble.new(object, k, self) + h[k] = VerifyingExistingMethodDouble.for(object, k, self) end end @@ -168,6 +168,25 @@ def with_signature def unimplemented? !@valid_method end + + def self.for(object, method_name, proxy) + if ClassNewMethodReference.applies_to?(method_name) { object } + VerifyingExistingClassNewMethodDouble + else + self + end.new(object, method_name, proxy) + end + end + + # Used in place of a `VerifyingExistingMethodDouble` for the specific case + # of mocking or stubbing a `new` method on a class. In this case, we substitute + # the method signature from `#initialize` since new's signature is just `*args`. + # + # @private + class VerifyingExistingClassNewMethodDouble < VerifyingExistingMethodDouble + def with_signature + yield Support::MethodSignature.new(object.instance_method(:initialize)) + end end end end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 7332e8cfd..9d8ec5eee 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -309,6 +309,9 @@ def implemented "works" end + def initialize(a, b) + end + def respond_to?(method_name, include_all=false) method_name.to_s == "dynamic_method" || super end @@ -329,7 +332,7 @@ def defined_private_method end end - let(:object) { klass.new } + let(:object) { klass.new(1, 2) } before do RSpec::Mocks.configuration.verify_partial_doubles = true @@ -410,6 +413,50 @@ def defined_private_method expect(object.implemented).to eq(:value) end + context "when `.new` is stubbed" do + before do + expect(klass.instance_method(:initialize).arity).to eq(2) + end + + it 'uses the method signature from `#initialize` for arg verification' do + prevents(/arguments/) { allow(klass).to receive(:new).with(1) } + allow(klass).to receive(:new).with(1, 2) + end + + context "on a class that has redefined `new`" do + it "uses the method signature of the redefined `new` for arg verification" do + subclass = Class.new(klass) do + def self.new(a); end + end + + prevents(/arguments/) { allow(subclass).to receive(:new).with(1, 2) } + allow(subclass).to receive(:new).with(1) + end + end + + context "on a class that has undefined `new`" do + it "prevents it from being stubbed" do + subclass = Class.new(klass) do + class << self + undef new + end + end + + prevents(/does not implement/) { allow(subclass).to receive(:new).with(1, 2) } + end + end + + context "on a class with a private `new`" do + it 'uses the method signature from `#initialize` for arg verification' do + subclass = Class.new(klass) do + private_class_method :new + end + + prevents(/arguments/) { allow(subclass).to receive(:new).with(1) } + allow(subclass).to receive(:new).with(1, 2) + end + end + end end end end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index 4ecd87976..47e5e79ab 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -115,6 +115,55 @@ module Mocks } end + context "when `.new` is stubbed" do + before do + expect(LoadedClass.instance_method(:initialize).arity).to eq(2) + end + + it 'uses the method signature from `#initialize` for arg verification' do + o = class_double(LoadedClass) + prevents(/arguments/) { allow(o).to receive(:new).with(1) } + allow(o).to receive(:new).with(1, 2) + end + + context "on a class that has redefined `new`" do + it "uses the method signature of the redefined `new` for arg verification" do + klass = Class.new(LoadedClass) do + def self.new(a); end + end + + o = class_double(klass) + prevents(/arguments/) { allow(o).to receive(:new).with(1, 2) } + allow(o).to receive(:new).with(1) + end + end + + context "on a class that has undefined `new`" do + it "prevents it from being stubbed" do + klass = Class.new(LoadedClass) do + class << self + undef new + end + end + + o = class_double(klass) + prevents(/does not implement/) { allow(o).to receive(:new).with(1, 2) } + end + end + + context "on a class with a private `new`" do + it 'uses the method signature from `#initialize` for arg verification' do + klass = Class.new(LoadedClass) do + private_class_method :new + end + + o = class_double(klass) + prevents(/arguments/) { allow(o).to receive(:new).with(1) } + allow(o).to receive(:new).with(1, 2) + end + end + end + context "when given an anonymous class" do it 'properly verifies' do subclass = Class.new(LoadedClass) diff --git a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb index 9694e75db..7d16ff2c4 100644 --- a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb @@ -3,6 +3,8 @@ module RSpec module Mocks RSpec.describe 'An object double' do + let(:loaded_instance) { LoadedClass.new(1, 2) } + it 'can replace an unloaded constant' do o = object_double("LoadedClass::NOINSTANCE").as_stubbed_const @@ -28,7 +30,7 @@ module Mocks end it 'can create a double that matches the interface of any arbitrary object' do - o = object_double(LoadedClass.new) + o = object_double(loaded_instance) prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_class_method) } @@ -49,7 +51,7 @@ module Mocks it 'does not allow as_stubbed_constant for real objects' do expect { - object_double(LoadedClass.new).as_stubbed_const + object_double(loaded_instance).as_stubbed_const }.to raise_error(/Can not perform constant replacement with an anonymous object/) end @@ -58,7 +60,7 @@ module Mocks end it 'validates `with` args against the method signature when stubbing a method' do - dbl = object_double(LoadedClass.new) + dbl = object_double(loaded_instance) prevents(/Wrong number of arguments. Expected 2, got 3./) { allow(dbl).to receive(:instance_method_with_two_args).with(3, :foo, :args) } diff --git a/spec/support/doubled_classes.rb b/spec/support/doubled_classes.rb index c83e9afdb..3150c9f29 100644 --- a/spec/support/doubled_classes.rb +++ b/spec/support/doubled_classes.rb @@ -5,6 +5,9 @@ class LoadedClass N = :n INSTANCE = LoadedClass.new + def initialize(a, b) + end + class << self def respond_to?(method_name, include_all = false) From 8056a9e99d776b46f4a52ed1e0afe5de95fd4db4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 4 Feb 2015 22:16:00 -0800 Subject: [PATCH 0831/1179] Pend specs to deal with jruby/jruby#2565. --- spec/rspec/mocks/partial_double_spec.rb | 2 ++ .../verifying_doubles/class_double_with_class_loaded_spec.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 9d8ec5eee..2fa12d4e9 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -448,6 +448,8 @@ class << self context "on a class with a private `new`" do it 'uses the method signature from `#initialize` for arg verification' do + pending "Failing on JRuby due to https://github.com/jruby/jruby/issues/2565" if RSpec::Support::Ruby.jruby? + subclass = Class.new(klass) do private_class_method :new end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index 47e5e79ab..f1c1d7f0a 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -153,6 +153,8 @@ class << self context "on a class with a private `new`" do it 'uses the method signature from `#initialize` for arg verification' do + pending "Failing on JRuby due to https://github.com/jruby/jruby/issues/2565" if RSpec::Support::Ruby.jruby? + klass = Class.new(LoadedClass) do private_class_method :new end From 7cd14ae5c054b824c80164e378fe393aea8cdde1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 8 Feb 2015 20:55:32 -0800 Subject: [PATCH 0832/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 14 +++++++++----- appveyor.yml | 4 ++-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 7 ++++--- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 3732f5298..25478aace 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 91d9e150c..d19d8f591 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -22,16 +22,20 @@ rvm: - 2.2 - ruby-head - ree - - jruby-18mode - - jruby - - jruby-head - rbx matrix: include: - rvm: jruby - env: JRUBY_OPTS='--2.0' + env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false -Xcompat.version=2.0' + - rvm: jruby-head + env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' + - rvm: jruby-18mode + env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' + - rvm: jruby + env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' allow_failures: - rvm: jruby-head + env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - rvm: ruby-head - rvm: rbx fast_finish: true diff --git a/appveyor.yml b/appveyor.yml index 55ad89c7d..7849f06d3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -29,6 +29,6 @@ test_script: environment: matrix: - - ruby_version: '200' + # ruby_version: '20' doesn't work for some reason - ruby_version: '193' - ruby_version: '21' diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 7ba918090..f2656ffb2 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index ac8bfa394..f73757bab 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,12 +1,13 @@ -# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $SCRIPT_DIR/travis_functions.sh source $SCRIPT_DIR/predicate_functions.sh -# idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html -export JRUBY_OPTS="${JRUBY_OPTS} -X-C" # disable JIT since these processes are so short lived +# If JRUBY_OPTS isn't set, use these. +# see http://docs.travis-ci.com/user/ci-environment/ +export JRUBY_OPTS=${JRUBY_OPTS:-"--server -Xcompile.invokedynamic=false"} SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 88f2c0f24..b41ef5e2d 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 2d081e216..83f9be429 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index a2c393362..5f29f7cf6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-03T22:13:58-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From a77686b50bd428b518141f73e04685117609002f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 9 Feb 2015 09:24:36 -0800 Subject: [PATCH 0833/1179] Remove unused method. --- lib/rspec/mocks/error_generator.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 04b2c4590..3e0194393 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -176,11 +176,6 @@ def raise_out_of_order_error(message) __raise "#{intro} received :#{message} out of order" end - # @private - def raise_block_failed_error(message, detail) - __raise "#{intro} received :#{message} but passed block failed with: #{detail}" - end - # @private def raise_missing_block_error(args_to_yield) __raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed" From 08eb0f2a16f04c01e3231c16dd5a64bdec486776 Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Mon, 9 Feb 2015 17:42:36 -0800 Subject: [PATCH 0834/1179] Fix argument formatting for received. https://github.com/rspec/rspec-mocks/issues/890 --- lib/rspec/mocks/error_generator.rb | 2 +- spec/rspec/mocks/matchers/receive_spec.rb | 25 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 3e0194393..ff1993d6b 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -142,7 +142,7 @@ def expected_part_of_expectation_error(expected_received_count, expectation_coun def actual_method_call_args_description(count, args) method_call_args_description(args) || if count > 0 && args.length > 0 - " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}" + " with arguments: #{format_args(*args)}" else "" end diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index a6c205862..a1744f744 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -298,11 +298,26 @@ def receiver.method_missing(*a); end # a poor man's stub... let(:receiver) { double } let(:wrapped) { expect(receiver) } - it 'sets up a message expectation that formats argument matchers correctly' do - wrapped.to receive(:foo).with an_instance_of Fixnum - expect { verify_all }.to( - raise_error(/expected: 1 time with arguments: \(an instance of Fixnum\)\n\s+received: 0 times$/) - ) + context "when a message is not received" do + it 'sets up a message expectation that formats argument matchers correctly' do + wrapped.to receive(:foo).with an_instance_of Fixnum + expect { verify_all }.to( + raise_error(/expected: 1 time with arguments: \(an instance of Fixnum\)\n\s+received: 0 times$/) + ) + end + end + + context "when a message is received the wrong number of times" do + it "sets up a message expectation that formats argument matchers correctly" do + wrapped.to receive(:foo).with(anything, hash_including(:bar => anything)) + + receiver.foo(1, :bar => 2) + receiver.foo(1, :bar => 3) + + expect { verify_all }.to( + raise_error(/received: 2 times with arguments: \(anything, hash_including\(:bar=>"anything"\)\)$/) + ) + end end end it_behaves_like "resets partial mocks cleanly" do From 7f6e065eb23d11a6be5dc0756915d6b9928ff663 Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Wed, 11 Feb 2015 10:28:24 -0800 Subject: [PATCH 0835/1179] Unify similar args description methods. ErrorGenerator#actual_method_call_args_description and ErrorGenerator#expected_method_call_args_description were almost identical. This change moves the common code from both into the ErrorGenerator#method_call_args_description method and yields to a block which lets the caller decide on what condition to show the formatted args. --- lib/rspec/mocks/error_generator.rb | 32 ++++++++++-------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index ff1993d6b..5c0a76884 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -129,40 +129,30 @@ def raise_expired_test_double_error # @private def received_part_of_expectation_error(actual_received_count, *args) "received: #{count_message(actual_received_count)}" + - actual_method_call_args_description(actual_received_count, args) + method_call_args_description(args) do + actual_received_count > 0 && args.length > 0 + end end # @private def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) "expected: #{count_message(expected_received_count, expectation_count_type)}" + - expected_method_call_args_description(argument_list_matcher.expected_args) - end - - # @private - def actual_method_call_args_description(count, args) - method_call_args_description(args) || - if count > 0 && args.length > 0 - " with arguments: #{format_args(*args)}" - else - "" + method_call_args_description(argument_list_matcher.expected_args) do + argument_list_matcher.expected_args.length > 0 end end # @private - def expected_method_call_args_description(args) - method_call_args_description(args) || - if args.length > 0 + def method_call_args_description(args, &block) + case args.first + when ArgumentMatchers::AnyArgsMatcher then " with any arguments" + when ArgumentMatchers::NoArgsMatcher then " with no arguments" + else + if block.call " with arguments: #{format_args(*args)}" else "" end - end - - # @private - def method_call_args_description(args) - case args.first - when ArgumentMatchers::AnyArgsMatcher then " with any arguments" - when ArgumentMatchers::NoArgsMatcher then " with no arguments" end end From 38281486875edfaee2fe1da2f9701814f60b8a88 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 12 Feb 2015 11:44:25 +1100 Subject: [PATCH 0836/1179] changelog for #891 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index ba3ebc8d7..f6b986db1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,8 @@ Enhancements: * When stubbing `new` on `MyClass` or `class_double(MyClass)`, use the method signature from `MyClass#initialize` to verify arguments. (Myron Marston, #886) +* Matcher descriptions used for `received` in too-many-calls failure output. + (Tim Wade, #891) ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) From ddb4c2089e2cddc593e1fd3c1da30d87621bec2c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 16 Feb 2015 21:30:36 -0800 Subject: [PATCH 0837/1179] Clarify changelog for #891. --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index f6b986db1..6e07c04a4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,8 +6,8 @@ Enhancements: * When stubbing `new` on `MyClass` or `class_double(MyClass)`, use the method signature from `MyClass#initialize` to verify arguments. (Myron Marston, #886) -* Matcher descriptions used for `received` in too-many-calls failure output. - (Tim Wade, #891) +* Use matcher descriptions when generating description of received arguments + for mock expectation failures. (Tim Wade, #891) ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) From b096ec98f7ea182761ea637be0e2637247fbfa6f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 13 Feb 2015 22:03:35 -0800 Subject: [PATCH 0838/1179] Cleanup whitespace. --- spec/rspec/mocks/argument_matchers_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index 4e1e41461..86b912227 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -123,7 +123,7 @@ module Mocks it "rejects goose when expecting a duck", :reset => true do expect(a_double).to receive(:random_call).with(duck_type(:abs, :div)) expect { - a_double.random_call("I don't respond to :abs or :div") + a_double.random_call("I don't respond to :abs or :div") }.to fail_matching "expected: (duck_type(:abs, :div))" end end From 3fff060a6f13f1193026998122c2f6ca63b58772 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 13 Feb 2015 22:10:40 -0800 Subject: [PATCH 0839/1179] Update to new rspec-support library wide checks. --- spec/rspec/mocks_spec.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 5f1f540f9..40de4b353 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -1,12 +1,14 @@ -require 'rspec/support/spec/prevent_load_time_warnings' +require 'rspec/support/spec/library_wide_checks' RSpec.describe RSpec::Mocks do - it_behaves_like 'a library that issues no warnings when loaded', - 'rspec-mocks', 'require "rspec/mocks"', - # Must be required before other files due to how our autoloads are setup. - # (Users won't hit this problem because they won't require all the files - # individually in whatever order the file system returns) - 'require "rspec/mocks/any_instance"' do + it_behaves_like 'library wide checks', 'rspec-mocks', + :preamble_for_lib => [ + 'require "rspec/mocks"', + # Must be required before other files due to how our autoloads are setup. + # (Users won't hit this problem because they won't require all the files + # individually in whatever order the file system returns) + 'require "rspec/mocks/any_instance"' + ], :allowed_loaded_feature_regexps => [/stringio/] do if RUBY_VERSION == '1.9.2' before(:example, :description => /spec files/) do From 394d3733f4825bd7f43beb23ad27ded6e414b6bf Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 13 Feb 2015 22:13:59 -0800 Subject: [PATCH 0840/1179] =?UTF-8?q?Don=E2=80=99t=20load=20stringio=20unn?= =?UTF-8?q?ecessarily.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Changelog.md | 1 + lib/rspec/mocks/verifying_double.rb | 11 +++++++++-- spec/rspec/mocks_spec.rb | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6e07c04a4..5ebf60752 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Enhancements: (Myron Marston, #886) * Use matcher descriptions when generating description of received arguments for mock expectation failures. (Tim Wade, #891) +* Avoid loading `stringio` unnecessarily. (Myron Marston, #894) ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index afd12d94d..7857a77a8 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -1,5 +1,4 @@ RSpec::Support.require_rspec_mocks 'verifying_proxy' -require 'stringio' module RSpec module Mocks @@ -35,8 +34,16 @@ def method_missing(message, *args, &block) super end + # @private + module SilentIO + def self.method_missing(*); end + def self.respond_to?(*) + true + end + end + # Redefining `__send__` causes ruby to issue a warning. - old, $stderr = $stderr, StringIO.new + old, $stderr = $stderr, SilentIO def __send__(name, *args, &block) @__sending_message = name super diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 40de4b353..d9df5403a 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -8,7 +8,7 @@ # (Users won't hit this problem because they won't require all the files # individually in whatever order the file system returns) 'require "rspec/mocks/any_instance"' - ], :allowed_loaded_feature_regexps => [/stringio/] do + ], :allowed_loaded_feature_regexps => [] do if RUBY_VERSION == '1.9.2' before(:example, :description => /spec files/) do From 0d3e0f8b89a4555cb207d75d85f3348b733f9bc7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 14 Feb 2015 09:36:51 -0800 Subject: [PATCH 0841/1179] Deal with the fact we load securerandom on 1.9.2. --- spec/rspec/mocks_spec.rb | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index d9df5403a..fe1d3cb4c 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -1,14 +1,22 @@ require 'rspec/support/spec/library_wide_checks' RSpec.describe RSpec::Mocks do - it_behaves_like 'library wide checks', 'rspec-mocks', - :preamble_for_lib => [ - 'require "rspec/mocks"', - # Must be required before other files due to how our autoloads are setup. - # (Users won't hit this problem because they won't require all the files - # individually in whatever order the file system returns) - 'require "rspec/mocks/any_instance"' - ], :allowed_loaded_feature_regexps => [] do + lib_preamble = [ + 'require "rspec/mocks"', + # Must be required before other files due to how our autoloads are setup. + # (Users won't hit this problem because they won't require all the files + # individually in whatever order the file system returns) + 'require "rspec/mocks/any_instance"' + ] + + # On 1.9.2 we load securerandom to get around the lack of `BasicObject#__id__. + # Loading securerandom loads many other stdlibs it depends on. Rather than + # declaring it (and all the stdlibs it loads) as allowed, it's easier just + # to prevent the loading of securerandom by faking out `BasicObject#__id__ + lib_preamble.unshift "class BasicObject; def __id__; end; end" if RUBY_VERSION == '1.9.2' + + it_behaves_like 'library wide checks', 'rspec-mocks', :preamble_for_lib => lib_preamble, + :allowed_loaded_feature_regexps => [] do if RUBY_VERSION == '1.9.2' before(:example, :description => /spec files/) do From fec1b7fc2b8583ca42d303c5b078b64d4d77bb32 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 16 Feb 2015 21:28:47 -0800 Subject: [PATCH 0842/1179] Update based on latest rspec-support requires. --- spec/rspec/mocks_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index fe1d3cb4c..10837da6d 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -16,7 +16,9 @@ lib_preamble.unshift "class BasicObject; def __id__; end; end" if RUBY_VERSION == '1.9.2' it_behaves_like 'library wide checks', 'rspec-mocks', :preamble_for_lib => lib_preamble, - :allowed_loaded_feature_regexps => [] do + :allowed_loaded_feature_regexps => [ + /rbconfig/ # loaded by rspec-support + ] do if RUBY_VERSION == '1.9.2' before(:example, :description => /spec files/) do From 4b1b00804776bf1caa9e311c831ba6eb08c718da Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Mon, 16 Feb 2015 13:40:32 -0800 Subject: [PATCH 0843/1179] Add more specific "does not implement" messages. The unimplemented error message gives no indication of whether the object is a class or instance double. This change passes that object to the error generator so that it can create a more specific message depending on whether it is an instance or class double. It defaults to the original " does not implement " for anything else. https://github.com/rspec/rspec-mocks/issues/838 --- features/verifying_doubles/class_doubles.feature | 2 +- .../verifying_doubles/instance_doubles.feature | 2 +- lib/rspec/mocks/error_generator.rb | 16 +++++++++++----- lib/rspec/mocks/verifying_proxy.rb | 3 ++- .../class_double_with_class_loaded_spec.rb | 2 +- .../instance_double_with_class_loaded_spec.rb | 4 ++-- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/features/verifying_doubles/class_doubles.feature b/features/verifying_doubles/class_doubles.feature index 8c94cd763..95aaf3316 100644 --- a/features/verifying_doubles/class_doubles.feature +++ b/features/verifying_doubles/class_doubles.feature @@ -67,4 +67,4 @@ Feature: Using a class double """ When I run `rspec spec/user_spec.rb` Then the output should contain "1 example, 1 failure" - And the output should contain "ConsoleNotifier does not implement:" + And the output should contain "the ConsoleNotifier class does not implement the class method:" diff --git a/features/verifying_doubles/instance_doubles.feature b/features/verifying_doubles/instance_doubles.feature index c234ca0e5..a8e0d5226 100644 --- a/features/verifying_doubles/instance_doubles.feature +++ b/features/verifying_doubles/instance_doubles.feature @@ -87,7 +87,7 @@ Feature: Using an instance double """ When I run `rspec -r./spec/spec_helper spec/unit/user_spec.rb` Then the output should contain "1 example, 1 failure" - And the output should contain "ConsoleNotifier does not implement:" + And the output should contain "ConsoleNotifier class does not implement the instance method:" Scenario: spec fails with dependencies loaded and incorrect arity Given a file named "app/models/console_notifier.rb" with: diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 5c0a76884..bde4d4322 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -98,11 +98,17 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc # rubocop:enable Style/ParameterLists # @private - def raise_unimplemented_error(doubled_module, method_name) - __raise "%s does not implement: %s" % [ - doubled_module.description, - method_name - ] + def raise_unimplemented_error(doubled_module, method_name, object) + message = case object + when InstanceVerifyingDouble + "the %s class does not implement the instance method: %s" + when ClassVerifyingDouble + "the %s class does not implement the class method: %s" + else + "%s does not implement: %s" + end + + __raise message % [doubled_module.description, method_name] end # @private diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 57f2df417..2e1a11377 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -25,7 +25,8 @@ def ensure_implemented(method_name) @error_generator.raise_unimplemented_error( @doubled_module, - method_name + method_name, + @object ) end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index f1c1d7f0a..5645d279e 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -147,7 +147,7 @@ class << self end o = class_double(klass) - prevents(/does not implement/) { allow(o).to receive(:new).with(1, 2) } + prevents(/does not implement the class method/) { allow(o).to receive(:new).with(1, 2) } end end diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index c0b1bb31a..b45c154e6 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -13,8 +13,8 @@ module Mocks o = instance_double('LoadedClass', :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) - prevents { allow(o).to receive(:undefined_instance_method) } - prevents { allow(o).to receive(:defined_class_method) } + prevents(/does not implement the instance method/) { allow(o).to receive(:undefined_instance_method) } + prevents(/does not implement the instance method/) { allow(o).to receive(:defined_class_method) } end it 'only allows instance methods that exist to be expected' do From 149293e5cca75b8da41d844747beaf76d850af7a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 18 Feb 2015 11:03:51 +1100 Subject: [PATCH 0844/1179] change for #896 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 5ebf60752..96ffb84e5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,8 @@ Enhancements: * Use matcher descriptions when generating description of received arguments for mock expectation failures. (Tim Wade, #891) * Avoid loading `stringio` unnecessarily. (Myron Marston, #894) +* Verifying doubles failure messages now distinguish between class and instance + level methods. (Tim Wade, #896) ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) From 8e56f8345c58848a16bc1c8de52b5b235f1db87b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 23 Feb 2015 10:02:45 -0800 Subject: [PATCH 0845/1179] Make doc string more accurate. --- spec/rspec/mocks/stub_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 760c97045..f7b42a46c 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -27,7 +27,7 @@ def existing_private_instance_method @stub = Object.new end - describe "using stub" do + describe "using `and_return`" do it "returns declared value when message is received" do allow(@instance).to receive(:msg).and_return(:return_value) expect(@instance.msg).to equal(:return_value) From 8d071d5ca1d3aba3b234db6852aa700a16114d92 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 23 Feb 2015 10:09:09 -0800 Subject: [PATCH 0846/1179] Stop calling methods on args passed to a stubbed method. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Any method call may mutate an object, so we can't safely call anything on a passed argument. This reverts the following PRs and commits: * #871: * 8a0962a6b8a896b1f1f2806a1282f29cde7d2baa ("Reword comment to not use “external” twice.") * 52c69f1ef8ac2c0de5ec08c72df709593a6a8ba5 ("Just use `Array#hash`.") * #868: * ed3fb3b1ac9e1187eeb44f4a68dd9878f9ce3ec6 ("Provide a clear error when received message args are mutated.") * fc9e9a59904ec52fa8e26e07ee65c41029ef18d0 ("Ignore arg mutations for method calls we are not concerned with.") This adds notes to the docs about using mutated args with `have_received(…).with(…)`. Fixes #892. --- README.md | 17 ++++++- features/basics/spies.feature | 1 + lib/rspec/mocks/error_generator.rb | 3 ++ lib/rspec/mocks/example_methods.rb | 3 ++ lib/rspec/mocks/message_expectation.rb | 11 ----- lib/rspec/mocks/proxy.rb | 46 ++----------------- .../mocks/matchers/have_received_spec.rb | 37 --------------- spec/rspec/mocks/stub_spec.rb | 20 ++++++++ 8 files changed, 46 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 4175ff854..e9a59557d 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,7 @@ class_spy("Invitation") # => same as `class_double("Invitation").as_null_object` object_spy("Invitation") # => same as `object_double("Invitation").as_null_object` ``` -Stubbing and verifying messages received in this way implements the Test Spy -pattern. +Verifying messages received in this way implements the Test Spy pattern. ```ruby invitation = spy('invitation') @@ -157,6 +156,20 @@ expect(invitation).to have_received(:accept).with(mailer) expect(invitation.accept).to eq(true) ``` +Note that `have_received(...).with(...)` is unable to work properly when +passed arguments are mutated after the spy records the received message. +For example, this does not work properly: + +```ruby +greeter = spy("greeter") + +message = "Hello" +greeter.greet_with(message) +message << ", World" + +expect(greeter).to have_received(:greet_with).with("Hello") +``` + ## Nomenclature ### Mock Objects and Test Stubs diff --git a/features/basics/spies.feature b/features/basics/spies.feature index 4f1b727e9..c2e468cea 100644 --- a/features/basics/spies.feature +++ b/features/basics/spies.feature @@ -13,6 +13,7 @@ Feature: Spies `have_received` supports the same fluent interface for [setting constraints](../setting-constraints) that normal message expectations do. Note: The `have_received` API shown here will only work if you are using rspec-expectations. + Note: `have_received(...).with(...)` is unable to work properly when passed arguments are mutated after the spy records the received message. Scenario: Using a spy Given a file named "spy_spec.rb" with: diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index bde4d4322..5dcc3d666 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -18,6 +18,9 @@ module Mocks # Raised for situations that RSpec cannot support due to mutations made # externally on arguments that RSpec is holding onto to use for later # comparisons. + # + # @deprecated We no longer raise this error but the constant remains until + # RSpec 4 for SemVer reasons. CannotSupportArgMutationsError = Class.new(StandardError) # @private diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 0ee49be71..d348eb228 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -278,6 +278,9 @@ def hide_const(constant_name) # # # You can also use most message expectations: # expect(invitation).to have_received(:accept).with(mailer).once + # + # @note `have_received(...).with(...)` is unable to work properly when + # passed arguments are mutated after the spy records the received message. def have_received(method_name, &block) Matchers::HaveReceived.new(method_name, &block) end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 532b26d3b..43d8479b9 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -510,17 +510,6 @@ def increase_actual_received_count! @actual_received_count += 1 end - def fail_if_problematic_received_arg_mutations(received_arg_list) - return if @argument_list_matcher == ArgumentListMatcher::MATCH_ALL - return unless received_arg_list.has_mutations? - - raise CannotSupportArgMutationsError, - "`have_received(...).with(...)` cannot be used when received " \ - "message args have later been mutated. You can use a normal " \ - "message expectation (`expect(...).to receive(...).with(...)`) " \ - "instead." - end - private def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index ca9a12e99..b936a3a62 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -88,12 +88,7 @@ def replay_received_message_on(expectation, &block) @error_generator.raise_expectation_on_unstubbed_method(expected_method_name) end - @messages_received.each do |(actual_method_name, received_arg_list, _)| - if expectation.message == actual_method_name - expectation.fail_if_problematic_received_arg_mutations(received_arg_list) - end - - args = received_arg_list.args + @messages_received.each do |(actual_method_name, args, _)| next unless expectation.matches?(actual_method_name, *args) expectation.safe_invoke(nil) @@ -103,8 +98,7 @@ def replay_received_message_on(expectation, &block) # @private def check_for_unexpected_arguments(expectation) - @messages_received.each do |(method_name, received_arg_list, _)| - args = received_arg_list.args + @messages_received.each do |(method_name, args, _)| next unless expectation.matches_name_but_not_args(method_name, *args) raise_unexpected_message_args_error(expectation, *args) @@ -144,11 +138,7 @@ def reset # @private def received_message?(method_name, *args, &block) - @messages_received.any? do |(received_method_name, received_arg_list, received_block)| - method_name == received_method_name && - args == received_arg_list.args && - block == received_block - end + @messages_received.any? { |array| array == [method_name, args, block] } end # @private @@ -159,35 +149,7 @@ def has_negative_expectation?(message) # @private def record_message_received(message, *args, &block) @order_group.invoked SpecificMessage.new(object, message, args) - @messages_received << [message, ReceivedArgList.new(args), block] - end - - class ReceivedArgList - attr_reader :args - - def initialize(args) - @args = args - @original_hash = hash_of(args) - end - - def has_mutations? - @original_hash != hash_of(args) - end - - private - - def hash_of(arg) - arg.hash - rescue Exception - # While `Object#hash` is a built-in ruby method that we expect args to - # support, there's no guarantee that all args will. For example, a - # `BasicObject` instance will raise a `NoMethodError`. Given that - # we use the hash only to advise the user of a rare case we don't - # support involving mutations, it seems better to ignore this error - # and use a static value in its place (which will make us assume no - # mutation has occurred). - :failed_to_get_hash - end + @messages_received << [message, args, block] end # @private diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 1d23b97fc..7bda18a3f 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -123,43 +123,6 @@ module Mocks expect(dbl).to have_received(:expected_method).with(:unexpected, :args) }.to raise_error(/with unexpected arguments/) end - - context "when a passed arg is later mutated before the `have_received(...).with(...)` expectation" do - it 'gives the user a clear error since we cannot support mutations' do - dbl = spy - dbl.foo(arg = "bar") - arg << "bazz" - - expect { - expect(dbl).to have_received(:foo).with("bar") - }.to raise_error(CannotSupportArgMutationsError) - end - - it 'does not prevent `have_received` without `with`' do - dbl = spy - dbl.foo(arg = "bar") - arg << "bazz" - - expect(dbl).to have_received(:foo) - end - - it 'does not prevent `have_received(...).with(...)` for a different method that has had no arg mutations' do - dbl = spy - dbl.foo(arg = "foo") - arg << "foo" - dbl.bar("bar") - - expect(dbl).to have_received(:bar).with("bar") - end - end - - it 'does not blow up when objects that do not support `#hash` are passed as args', :if => defined?(::BasicObject) do - dbl = spy - dbl.foo(arg = ::BasicObject.new) - expect { arg.hash }.to raise_error(NoMethodError) - - expect(dbl).to have_received(:foo).with(arg) - end end it 'generates a useful description' do diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index f7b42a46c..d210a9569 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -102,6 +102,26 @@ def existing_private_instance_method end end + context "when the stubbed method is called" do + it "does not call any methods on the passed args, since that could mutate them", :issue => 892 do + recorder = Class.new(defined?(::BasicObject) ? ::BasicObject : ::Object) do + def called_methods + @called_methods ||= [] + end + + def method_missing(name, *) + called_methods << name + self + end + end.new + + allow(@stub).to receive(:foo) + expect { + @stub.foo(recorder) + }.not_to change(recorder, :called_methods) + end + end + context "stubbing with prepend", :if => Support::RubyFeatures.module_prepends_supported? do module ToBePrepended def value From 62236cd4ec8acbba7a7b32b263d52b707fddd187 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 23 Feb 2015 10:52:13 -0800 Subject: [PATCH 0847/1179] Fixup formatting of `@example` YARD comments. - No blank line after `@example`; YARD renders that as a blank line of code. - Standardize on 2 spaces. --- lib/rspec/mocks/argument_matchers.rb | 11 +------- lib/rspec/mocks/configuration.rb | 5 ---- lib/rspec/mocks/example_methods.rb | 25 +++++------------- lib/rspec/mocks/syntax.rb | 38 +++++++++++++--------------- 4 files changed, 25 insertions(+), 54 deletions(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 5114a0cb2..8fcfd02e8 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -17,7 +17,6 @@ module ArgumentMatchers # Acts like an arg splat, matching any number of args at any point in an arg list. # # @example - # # expect(object).to receive(:message).with(1, 2, any_args) # # # matches any of these: @@ -31,7 +30,6 @@ def any_args # Matches any argument at all. # # @example - # # expect(object).to receive(:message).with(anything) def anything AnyArgMatcher::INSTANCE @@ -40,7 +38,6 @@ def anything # Matches no arguments. # # @example - # # expect(object).to receive(:message).with(no_args) def no_args NoArgsMatcher::INSTANCE @@ -49,7 +46,6 @@ def no_args # Matches if the actual argument responds to the specified messages. # # @example - # # expect(object).to receive(:message).with(duck_type(:hello)) # expect(object).to receive(:message).with(duck_type(:hello, :goodbye)) def duck_type(*args) @@ -59,7 +55,6 @@ def duck_type(*args) # Matches a boolean value. # # @example - # # expect(object).to receive(:message).with(boolean()) def boolean BooleanMatcher::INSTANCE @@ -69,7 +64,6 @@ def boolean # Ignores any additional keys. # # @example - # # expect(object).to receive(:message).with(hash_including(:key => val)) # expect(object).to receive(:message).with(hash_including(:key)) # expect(object).to receive(:message).with(hash_including(:key, :key2 => val2)) @@ -81,7 +75,6 @@ def hash_including(*args) # Ignores duplicates and additional values # # @example - # # expect(object).to receive(:message).with(array_including(1,2,3)) # expect(object).to receive(:message).with(array_including([1,2,3])) def array_including(*args) @@ -92,7 +85,6 @@ def array_including(*args) # Matches a hash that doesn't include the specified key(s) or key/value. # # @example - # # expect(object).to receive(:message).with(hash_excluding(:key => val)) # expect(object).to receive(:message).with(hash_excluding(:key)) # expect(object).to receive(:message).with(hash_excluding(:key, :key2 => :val2)) @@ -105,7 +97,6 @@ def hash_excluding(*args) # Matches if `arg.instance_of?(klass)` # # @example - # # expect(object).to receive(:message).with(instance_of(Thing)) def instance_of(klass) InstanceOf.new(klass) @@ -114,8 +105,8 @@ def instance_of(klass) alias_method :an_instance_of, :instance_of # Matches if `arg.kind_of?(klass)` - # @example # + # @example # expect(object).to receive(:message).with(kind_of(Thing)) def kind_of(klass) KindOf.new(klass) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 73d48dc34..28b22b879 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -19,7 +19,6 @@ def yield_receiver_to_any_instance_implementation_blocks? # Defaults to `true`. # # @example - # # RSpec.configure do |rspec| # rspec.mock_with :rspec do |mocks| # mocks.yield_receiver_to_any_instance_implementation_blocks = false @@ -35,7 +34,6 @@ def yield_receiver_to_any_instance_implementation_blocks? # the process. # # @example - # # RSpec.configure do |rspec| # rspec.mock_with :rspec do |mocks| # mocks.add_stub_and_should_receive_to Delegator @@ -55,7 +53,6 @@ def add_stub_and_should_receive_to(*modules) # disable `expect` syntax. # # @example - # # RSpec.configure do |rspec| # rspec.mock_with :rspec do |mocks| # mocks.syntax = [:expect, :should] @@ -81,7 +78,6 @@ def syntax=(*values) # that are enabled. # # @example - # # unless RSpec::Mocks.configuration.syntax.include?(:expect) # raise "this RSpec extension gem requires the rspec-mocks `:expect` syntax" # end @@ -107,7 +103,6 @@ def verify_doubled_constant_names? # Provides a way to perform customisations when verifying doubles. # # @example - # # RSpec::Mocks.configuration.when_declaring_verifying_double do |ref| # ref.some_method! # end diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index d348eb228..04c4ca9b2 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -24,7 +24,6 @@ module ExampleMethods # hash of message/return-value pairs. # # @example - # # book = double("book", :title => "The RSpec Book") # book.title #=> "The RSpec Book" # @@ -219,7 +218,6 @@ def allow_message_expectations_on_nil # @return [Object] the stubbed value of the constant # # @example - # # stub_const("MyClass", Class.new) # => Replaces (or defines) MyClass with a new class object. # stub_const("SomeModel::PER_PAGE", 5) # => Sets SomeModel::PER_PAGE to 5. # @@ -253,7 +251,6 @@ def stub_const(constant_name, value, options={}) # The current constant scoping at the point of call is not considered. # # @example - # # hide_const("MyClass") # => MyClass is now an undefined constant def hide_const(constant_name) ConstantMutator.hide(constant_name) @@ -271,7 +268,6 @@ def hide_const(constant_name) # called. # # @example - # # invitation = double('invitation', accept: true) # user.accept_invitation(invitation) # expect(invitation).to have_received(:accept) @@ -290,7 +286,6 @@ def have_received(method_name, &block) # on it. # # @example - # # expect(obj).to receive(:foo).with(5).and_return(:return_value) # # @note This method is usually provided by rspec-expectations. However, @@ -303,7 +298,6 @@ def have_received(method_name, &block) # on it. # # @example - # # allow(dbl).to receive(:foo).with(5).and_return(:return_value) # # @note If you disable the `:expect` syntax this method will be undefined. @@ -313,7 +307,6 @@ def have_received(method_name, &block) # on instances of it. # # @example - # # expect_any_instance_of(MyClass).to receive(:foo) # # @note If you disable the `:expect` syntax this method will be undefined. @@ -323,7 +316,6 @@ def have_received(method_name, &block) # on instances of it. # # @example - # # allow_any_instance_of(MyClass).to receive(:foo) # # @note This is only available when you have enabled the `expect` syntax. @@ -336,7 +328,6 @@ def have_received(method_name, &block) # times, and configure how the object should respond to the message. # # @example - # # expect(obj).to receive(:hello).with("world").exactly(3).times # # @note If you disable the `:expect` syntax this method will be undefined. @@ -349,7 +340,6 @@ def have_received(method_name, &block) # interface. # # @example - # # allow(obj).to receive_messages(:speak => "Hello World") # allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow") # @@ -373,16 +363,15 @@ def have_received(method_name, &block) # implementation calls `foo.baz.bar`, the stub will not work. # # @example + # allow(double).to receive_message_chain("foo.bar") { :baz } + # allow(double).to receive_message_chain(:foo, :bar => :baz) + # allow(double).to receive_message_chain(:foo, :bar) { :baz } # - # allow(double).to receive_message_chain("foo.bar") { :baz } - # allow(double).to receive_message_chain(:foo, :bar => :baz) - # allow(double).to receive_message_chain(:foo, :bar) { :baz } - # - # # Given any of ^^ these three forms ^^: - # double.foo.bar # => :baz + # # Given any of ^^ these three forms ^^: + # double.foo.bar # => :baz # - # # Common use in Rails/ActiveRecord: - # allow(Article).to receive_message_chain("recent.published") { [Article.new] } + # # Common use in Rails/ActiveRecord: + # allow(Article).to receive_message_chain("recent.published") { [Article.new] } # # @note If you disable the `:expect` syntax this method will be undefined. diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index a2a28b70f..1ba4dcd64 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -214,11 +214,10 @@ class BasicObject # the end of the example. # # @example - # - # logger = double('logger') - # thing_that_logs = ThingThatLogs.new(logger) - # logger.should_receive(:log) - # thing_that_logs.do_something_that_logs_a_message + # logger = double('logger') + # thing_that_logs = ThingThatLogs.new(logger) + # logger.should_receive(:log) + # thing_that_logs.do_something_that_logs_a_message # # @note This is only available when you have enabled the `should` syntax. # @see RSpec::Mocks::ExampleMethods#expect @@ -232,10 +231,9 @@ class BasicObject # Tells the object to respond to the message with the specified value. # # @example - # - # counter.stub(:count).and_return(37) - # counter.stub(:count => 37) - # counter.stub(:count) { 37 } + # counter.stub(:count).and_return(37) + # counter.stub(:count => 37) + # counter.stub(:count) { 37 } # # @note This is only available when you have enabled the `should` syntax. # @see RSpec::Mocks::ExampleMethods#allow @@ -269,10 +267,9 @@ class BasicObject # implementation calls `foo.baz.bar`, the stub will not work. # # @example - # - # double.stub_chain("foo.bar") { :baz } - # double.stub_chain(:foo, :bar => :baz) - # double.stub_chain(:foo, :bar) { :baz } + # double.stub_chain("foo.bar") { :baz } + # double.stub_chain(:foo, :bar => :baz) + # double.stub_chain(:foo, :bar) { :baz } # # # Given any of ^^ these three forms ^^: # double.foo.bar # => :baz @@ -311,15 +308,14 @@ class Class # class. # # @example + # Car.any_instance.should_receive(:go) + # race = Race.new + # race.cars << Car.new + # race.go # assuming this delegates to all of its cars + # # this example would pass # - # Car.any_instance.should_receive(:go) - # race = Race.new - # race.cars << Car.new - # race.go # assuming this delegates to all of its cars - # # this example would pass - # - # Account.any_instance.stub(:balance) { Money.new(:USD, 25) } - # Account.new.balance # => Money.new(:USD, 25)) + # Account.any_instance.stub(:balance) { Money.new(:USD, 25) } + # Account.new.balance # => Money.new(:USD, 25)) # # @return [Recorder] # From c455fc5dcff64c81ce3688608a9f90c5d0157a0f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 23 Feb 2015 19:26:06 -0800 Subject: [PATCH 0848/1179] Forwardport 3.2.1 release notes. --- Changelog.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 96ffb84e5..ceb575151 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.3.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.1...master) Enhancements: @@ -12,6 +12,19 @@ Enhancements: * Verifying doubles failure messages now distinguish between class and instance level methods. (Tim Wade, #896) +### 3.2.1 / 2015-02-23 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) + +Bug Fixes: + +* Add missing `rspec/support/differ` require so that rspec-mocks can be + used w/o rspec-expectations (which also loads the differ and hided the + fact we forgot to require it). (Myron Marston, #893) +* Revert tracking of received arg mutation (added in 3.2.0 to provide an + error in a situation we can't support) as our implementation has side + effects on non-standard objects and there's no solution we could come + up with that always works. (Myron Marston, #900) + ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) From d437b0334388abffeb0a2667face17276fa8b18b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 24 Feb 2015 14:46:39 -0800 Subject: [PATCH 0849/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 6 +++++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 4 ++-- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 25478aace..9e52c97e3 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index d19d8f591..636f13402 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -39,3 +39,7 @@ matrix: - rvm: ruby-head - rvm: rbx fast_finish: true +branches: + only: + - master + - /^\d+-\d+-maintenance$/ diff --git a/appveyor.yml b/appveyor.yml index 7849f06d3..15461acc3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f2656ffb2..c7fae3a8a 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index f73757bab..1faf04ad5 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -113,7 +113,7 @@ function check_documentation_coverage { } function check_style_and_lint { - echo "bin/rubucop lib" + echo "bin/rubocop lib" bin/rubocop lib } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index b41ef5e2d..1c9275fbf 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 83f9be429..e78cbd843 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 5f29f7cf6..8e86947b7 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-08T20:55:32-08:00 from the rspec-dev repo. +# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 6c0c8d1c47ab8ad7c970ba3830ee6966721be28e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 5 Mar 2015 20:05:29 -0800 Subject: [PATCH 0850/1179] Simplify `and_raise`. Passing args directly to `raise` is much simpler :). --- lib/rspec/mocks/message_expectation.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 43d8479b9..0940e61f1 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -142,13 +142,9 @@ def and_wrap_original(&block) # allow(car).to receive(:go).and_raise(OutOfGas) # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive") # allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz)) - def and_raise(exception=RuntimeError, message=nil) + def and_raise(*args) raise_already_invoked_error_if_necessary(__method__) - if exception.respond_to?(:exception) - exception = message ? exception.exception(message) : exception.exception - end - - self.terminal_implementation_action = Proc.new { raise exception } + self.terminal_implementation_action = Proc.new { raise(*args) } nil end From ad0c0ac2a45c3fa686aed7d444ff1e1d6d8ff61d Mon Sep 17 00:00:00 2001 From: Nicholas Henry Date: Sat, 7 Mar 2015 08:12:32 -0500 Subject: [PATCH 0851/1179] Fix broken links in 'Further Reading' --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9a59557d..701011723 100644 --- a/README.md +++ b/README.md @@ -417,8 +417,8 @@ There are many different viewpoints about the meaning of mocks and stubs. If you are interested in learning more, here is some recommended reading: * Mock Objects: http://www.mockobjects.com/ -* Endo-Testing: http://stalatest.googlecode.com/svn/trunk/Literatur/mockobjects.pdf -* Mock Roles, Not Objects: http://jmock.org/oopsla2004.pdf +* Endo-Testing: http://www.ccs.neu.edu/research/demeter/related-work/extreme-programming/MockObjectsFinal.PDF +* Mock Roles, Not Objects: http://www.jmock.org/oopsla2004.pdf * Test Double: http://www.martinfowler.com/bliki/TestDouble.html * Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html * Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html From cd51da8ec07349f0e8257e36fa38ee2dc94605cf Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Mon, 9 Mar 2015 12:58:17 -0700 Subject: [PATCH 0852/1179] Provide a message if the wrong double is used. If an undefined method is stubbed on an instance double, the error generator will check to see if it is defined on the class level and provide a message to indicate that this may be what was meant. It also does the converse for class doubles and instance methods. https://github.com/rspec/rspec-mocks/issues/838 --- lib/rspec/mocks/error_generator.rb | 14 ++++++++++++-- .../class_double_with_class_loaded_spec.rb | 12 ++++++++++++ .../instance_double_with_class_loaded_spec.rb | 12 ++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 5dcc3d666..86622159c 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -104,9 +104,19 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc def raise_unimplemented_error(doubled_module, method_name, object) message = case object when InstanceVerifyingDouble - "the %s class does not implement the instance method: %s" + "the %s class does not implement the instance method: %s" << + if ObjectMethodReference.for(doubled_module, method_name).implemented? + ". Perhaps you meant to use `class_double` instead?" + else + "" + end when ClassVerifyingDouble - "the %s class does not implement the class method: %s" + "the %s class does not implement the class method: %s" << + if InstanceMethodReference.for(doubled_module, method_name).implemented? + ". Perhaps you meant to use `instance_double` instead?" + else + "" + end else "%s does not implement: %s" end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index 5645d279e..c76c76f31 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -28,6 +28,18 @@ module Mocks prevents { expect(o).to receive(:defined_instance_method) } end + USE_INSTANCE_DOUBLE_MSG = "Perhaps you meant to use `instance_double`" + + it "suggests using `instance_double` when an instance method is stubbed" do + o = class_double("LoadedClass") + prevents(a_string_including(USE_INSTANCE_DOUBLE_MSG)) { allow(o).to receive(:defined_instance_method) } + end + + it "doesn't suggest `instance_double` when a non-instance method is stubbed'" do + o = class_double("LoadedClass") + prevents(a_string_excluding(USE_INSTANCE_DOUBLE_MSG)) { allow(o).to receive(:undefined_instance_method) } + end + it 'gives a descriptive error message for NoMethodError' do o = class_double("LoadedClass") expect { diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index b45c154e6..41f6a92d6 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -28,6 +28,18 @@ module Mocks prevents { expect(o).to receive(:defined_class_method) } end + USE_CLASS_DOUBLE_MSG = "Perhaps you meant to use `class_double`" + + it "suggests using `class_double` when a class method is stubbed" do + o = instance_double("LoadedClass") + prevents(a_string_including(USE_CLASS_DOUBLE_MSG)) { allow(o).to receive(:defined_class_method) } + end + + it "doesn't suggest `class_double` when a non-class method is stubbed" do + o = instance_double("LoadedClass") + prevents(a_string_excluding(USE_CLASS_DOUBLE_MSG)) { allow(o).to receive(:undefined_class_method) } + end + it 'allows `send` to be stubbed if it is defined on the class' do o = instance_double('LoadedClass') allow(o).to receive(:send).and_return("received") From fbaf33e41af4f9a888e2a04185370ad0be0e25e8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 10 Mar 2015 13:25:07 -0700 Subject: [PATCH 0853/1179] Update changelog to mention #908. [ci skip] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index ceb575151..00a850193 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,7 @@ Enhancements: for mock expectation failures. (Tim Wade, #891) * Avoid loading `stringio` unnecessarily. (Myron Marston, #894) * Verifying doubles failure messages now distinguish between class and instance - level methods. (Tim Wade, #896) + level methods. (Tim Wade, #896, #908) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) From 7b13b850aeb69e9374100e10e2dd216616a37e89 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 15 Mar 2015 16:56:36 +1100 Subject: [PATCH 0854/1179] correct documentation to highlight the expected behaviour [skip ci] --- features/working_with_legacy_code/any_instance.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/working_with_legacy_code/any_instance.feature b/features/working_with_legacy_code/any_instance.feature index 1b58a2dd9..757af13e0 100644 --- a/features/working_with_legacy_code/any_instance.feature +++ b/features/working_with_legacy_code/any_instance.feature @@ -17,7 +17,7 @@ Feature: Any Instance * The `rspec-mocks` API is designed for individual object instances, but this feature operates on entire classes of objects. As a result there are some semantically confusing edge cases. For example, in `expect_any_instance_of(Widget).to - receive(:name).twice` it isn't clear whether each specific instance is expected to + receive(:name).twice` it isn't clear whether a specific instance is expected to receive `name` twice, or if two receives total are expected. (It's the former.) * Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too complex. From 0a007fa8455df7bcea4d364ead0e154fb6fa6489 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 15 Mar 2015 22:57:16 -0700 Subject: [PATCH 0855/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 4 ++-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 3 ++- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 9e52c97e3..5a3a5b0ca 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. +# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 636f13402..8a9d1e33c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. +# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 15461acc3..668e41645 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. +# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -25,7 +25,7 @@ install: - cinst ansicon test_script: - - bundle exec rspec + - bundle exec rspec --backtrace environment: matrix: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index c7fae3a8a..c0bf6b51e 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. +# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 1faf04ad5..30b0b4269 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. +# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -60,6 +60,7 @@ function run_specs_one_by_one { echo "Running each spec file, one-by-one..." for file in `find spec -iname '*_spec.rb'`; do + echo "Running $file" bin/rspec $file -b --format progress done } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 1c9275fbf..f4478edf1 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. +# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index e78cbd843..26f1b82f4 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. +# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 8e86947b7..5c3691fb9 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-02-24T14:46:39-08:00 from the rspec-dev repo. +# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From e14766514fc802c6750137555928cd4edd51a677 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Mon, 2 Feb 2015 17:55:56 +0000 Subject: [PATCH 0856/1179] Make message expectations raise at the end of tests even if they have eagerly raised. --- lib/rspec/mocks/message_expectation.rb | 5 +- spec/rspec/mocks/any_instance_spec.rb | 61 ++++++++++--------- spec/rspec/mocks/at_most_spec.rb | 18 +++--- spec/rspec/mocks/matchers/receive_spec.rb | 32 +++++----- .../mocks/mock_expectation_error_spec.rb | 5 +- .../rspec/mocks/multiple_return_value_spec.rb | 5 +- spec/rspec/mocks/once_counts_spec.rb | 4 +- spec/rspec/mocks/partial_double_spec.rb | 16 ++--- spec/rspec/mocks/precise_counts_spec.rb | 4 +- .../mocks/reraising_eager_raises_spec.rb | 49 +++++++++++++++ spec/rspec/mocks/should_syntax_spec.rb | 19 +++--- .../syntax_agnostic_message_matchers_spec.rb | 4 +- spec/rspec/mocks/thrice_counts_spec.rb | 4 +- spec/rspec/mocks/twice_counts_spec.rb | 4 +- .../instance_double_with_class_loaded_spec.rb | 3 + spec/spec_helper.rb | 6 ++ spec/support/matchers.rb | 2 +- 17 files changed, 156 insertions(+), 85 deletions(-) create mode 100644 spec/rspec/mocks/reraising_eager_raises_spec.rb diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 0940e61f1..f7a5edc23 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -422,9 +422,8 @@ def matches_name_but_not_args(message, *args) end def verify_messages_received - InsertOntoBacktrace.line(@expected_from) do - generate_error unless expected_messages_received? || failed_fast? - end + return if expected_messages_received? + InsertOntoBacktrace.line(@expected_from) { generate_error } end def expected_messages_received? diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 6555e61e7..6303bc29b 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -380,7 +380,10 @@ class RSpec::SampleRspecTestClass;end context "expect_any_instance_of(...).not_to receive" do it "fails if the method is called" do expect_any_instance_of(klass).not_to receive(:existing_method) - expect { klass.new.existing_method }.to raise_error(RSpec::Mocks::MockExpectationError) + + expect_fast_failure_from(klass.new) do |instance| + instance.existing_method + end end it "passes if no method is called" do @@ -398,15 +401,18 @@ class RSpec::SampleRspecTestClass;end allow_any_instance_of(klass).to receive(:foo).and_return(1) expect(instance.foo).to eq(1) expect_any_instance_of(klass).not_to receive(:foo) - expect { instance.foo }.to fail + + expect_fast_failure_from(instance) do + instance.foo + end end context "with constraints" do it "fails if the method is called with the specified parameters" do expect_any_instance_of(klass).not_to receive(:existing_method_with_arguments).with(:argument_one, :argument_two) - expect { - klass.new.existing_method_with_arguments(:argument_one, :argument_two) - }.to raise_error(RSpec::Mocks::MockExpectationError) + expect_fast_failure_from(klass.new) do |instance| + instance.existing_method_with_arguments(:argument_one, :argument_two) + end end it "passes if the method is called with different parameters" do @@ -777,12 +783,12 @@ def foo; end end it "fails for more than one invocation" do - expect do - expect_any_instance_of(klass).to receive(:foo).once - instance = klass.new + expect_any_instance_of(klass).to receive(:foo).once + + expect_fast_failure_from(klass.new) do |instance| 2.times { instance.foo } verify instance - end.to raise_error(RSpec::Mocks::MockExpectationError) + end end end @@ -794,12 +800,12 @@ def foo; end end it "fails for more than two invocations" do - expect do - expect_any_instance_of(klass).to receive(:foo).twice - instance = klass.new + expect_any_instance_of(klass).to receive(:foo).twice + + expect_fast_failure_from(klass.new) do |instance| 3.times { instance.foo } verify instance - end.to raise_error(RSpec::Mocks::MockExpectationError) + end end end @@ -811,12 +817,11 @@ def foo; end end it "fails for more than three invocations" do - expect do - expect_any_instance_of(klass).to receive(:foo).thrice - instance = klass.new + expect_any_instance_of(klass).to receive(:foo).thrice + expect_fast_failure_from(klass.new) do |instance| 4.times { instance.foo } verify instance - end.to raise_error(RSpec::Mocks::MockExpectationError) + end end it "fails for less than three invocations" do @@ -846,12 +851,11 @@ def foo; end end it "fails for n invocations where n > 3" do - expect do - expect_any_instance_of(klass).to receive(:foo).exactly(3).times - instance = klass.new + expect_any_instance_of(klass).to receive(:foo).exactly(3).times + expect_fast_failure_from(klass.new) do |instance| 4.times { instance.foo } verify instance - end.to raise_error(RSpec::Mocks::MockExpectationError) + end end end @@ -892,12 +896,11 @@ def foo; end end it "fails for n invocations where n > 3" do - expect do - expect_any_instance_of(klass).to receive(:foo).at_most(3).times - instance = klass.new + expect_any_instance_of(klass).to receive(:foo).at_most(3).times + expect_fast_failure_from(klass.new) do |instance| 4.times { instance.foo } verify instance - end.to raise_error(RSpec::Mocks::MockExpectationError) + end end end @@ -908,10 +911,10 @@ def foo; end end it "fails on the first invocation" do - expect do - expect_any_instance_of(klass).to receive(:foo).never - klass.new.foo - end.to raise_error(RSpec::Mocks::MockExpectationError) + expect_any_instance_of(klass).to receive(:foo).never + expect_fast_failure_from(klass.new) do |instance| + instance.foo + end end context "when combined with other expectations" do diff --git a/spec/rspec/mocks/at_most_spec.rb b/spec/rspec/mocks/at_most_spec.rb index 3442e678b..cb5fe7271 100644 --- a/spec/rspec/mocks/at_most_spec.rb +++ b/spec/rspec/mocks/at_most_spec.rb @@ -76,26 +76,27 @@ module Mocks expect(@double).to receive(:do_something).at_most(2).times @double.do_something @double.do_something - expect { + + expect_fast_failure_from(@double, /expected: at most 2 times.*received: 3 times/m) do @double.do_something - }.to raise_error(/expected: at most 2 times.*received: 3 times/m) + end end it "fails fast when at_most(:once) and is called twice" do expect(@double).to receive(:do_something).at_most(:once) @double.do_something - expect { + expect_fast_failure_from(@double, /expected: at most 1 time.*received: 2 times/m) do @double.do_something - }.to raise_error(/expected: at most 1 time.*received: 2 times/m) + end end it "fails fast when at_most(:twice) and is called three times" do expect(@double).to receive(:do_something).at_most(:twice) @double.do_something @double.do_something - expect { + expect_fast_failure_from(@double, /expected: at most 2 times.*received: 3 times/m) do @double.do_something - }.to raise_error(/expected: at most 2 times.*received: 3 times/m) + end end it "fails fast when at_most(:thrice) and is called four times" do @@ -103,11 +104,10 @@ module Mocks @double.do_something @double.do_something @double.do_something - expect { + expect_fast_failure_from(@double, /expected: at most 3 times.*received: 4 times/m) do @double.do_something - }.to raise_error(/expected: at most 3 times.*received: 4 times/m) + end end - end end end diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index a1744f744..e33822a69 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -183,17 +183,17 @@ module Mocks it 'sets up a negative message expectation that fails if the message is received' do wrapped.not_to receive(:foo) - expect { + expect_fast_failure_from(receiver, /expected: 0 times.*received: 1 time/m) do receiver.foo - }.to raise_error(/expected: 0 times.*received: 1 time/m) + end end it 'supports `to_not` as an alias for `not_to`' do wrapped.to_not receive(:foo) - expect { + expect_fast_failure_from(receiver, /expected: 0 times.*received: 1 time/m) do receiver.foo - }.to raise_error(/expected: 0 times.*received: 1 time/m) + end end it 'allows the caller to constrain the received arguments' do @@ -204,9 +204,9 @@ def receiver.method_missing(*a); end # a poor man's stub... receiver.foo(:b) }.not_to raise_error - expect { + expect_fast_failure_from(receiver, /expected: 0 times.*received: 1 time/m) do receiver.foo(:a) - }.to raise_error(/expected: 0 times.*received: 1 time/m) + end end it 'prevents confusing double-negative expressions involving `never`' do @@ -453,23 +453,23 @@ def receiver.method_missing(*a); end # a poor man's stub... end it 'supports `expect(...).not_to receive`' do - dbl = double + expect_fast_failure_from(double) do |dbl| + framework.new.instance_exec do + expect(dbl).not_to receive(:foo) + end - framework.new.instance_exec do - expect(dbl).not_to receive(:foo) + dbl.foo end - - expect { dbl.foo }.to raise_error(RSpec::Mocks::MockExpectationError) end it 'supports `expect(...).to_not receive`' do - dbl = double + expect_fast_failure_from(double) do |dbl| + framework.new.instance_exec do + expect(dbl).to_not receive(:foo) + end - framework.new.instance_exec do - expect(dbl).to_not receive(:foo) + dbl.foo end - - expect { dbl.foo }.to raise_error(RSpec::Mocks::MockExpectationError) end end diff --git a/spec/rspec/mocks/mock_expectation_error_spec.rb b/spec/rspec/mocks/mock_expectation_error_spec.rb index 49211d5f4..77bd6e922 100644 --- a/spec/rspec/mocks/mock_expectation_error_spec.rb +++ b/spec/rspec/mocks/mock_expectation_error_spec.rb @@ -11,9 +11,10 @@ def self.foo it 'is not caught by StandardError rescue blocks' do expect(Foo).not_to receive(:bar) - expect { + + expect_fast_failure_from(Foo) do Foo.foo - }.to raise_error(RSpec::Mocks::MockExpectationError) + end end end end diff --git a/spec/rspec/mocks/multiple_return_value_spec.rb b/spec/rspec/mocks/multiple_return_value_spec.rb index 6fc86f114..42a858901 100644 --- a/spec/rspec/mocks/multiple_return_value_spec.rb +++ b/spec/rspec/mocks/multiple_return_value_spec.rb @@ -123,7 +123,10 @@ module Mocks @double.do_something @double.do_something @double.do_something - expect { @double.do_something }.to raise_error + + expect_fast_failure_from(@double) do + @double.do_something + end end end end diff --git a/spec/rspec/mocks/once_counts_spec.rb b/spec/rspec/mocks/once_counts_spec.rb index fd4ae01ba..eb368b44d 100644 --- a/spec/rspec/mocks/once_counts_spec.rb +++ b/spec/rspec/mocks/once_counts_spec.rb @@ -34,9 +34,9 @@ module Mocks it "fails fast when called twice" do expect(@double).to receive(:do_something).once @double.do_something - expect { + expect_fast_failure_from(@double) do @double.do_something - }.to raise_error(RSpec::Mocks::MockExpectationError) + end end it "fails when not called" do diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 2fa12d4e9..1233ce6a8 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -45,12 +45,12 @@ module Mocks it "can disallow messages from being received" do expect(object).not_to receive(:fuhbar) - expect { - object.fuhbar - }.to raise_error( - RSpec::Mocks::MockExpectationError, + expect_fast_failure_from( + object, /expected\: 0 times with any arguments\n received\: 1 time/ - ) + ) do + object.fuhbar + end end it "can expect a message and set a return value" do @@ -88,9 +88,10 @@ module Mocks it "can accept the string form of a message for a negative message expectation" do expect(object).not_to receive('foobar') - expect { + + expect_fast_failure_from(object) do object.foobar - }.to raise_error(RSpec::Mocks::MockExpectationError) + end end it "uses reports nil in the error message" do @@ -360,6 +361,7 @@ def defined_private_method it 'verifies arity range when matching arguments' do prevents { expect(object).to receive(:implemented).with('bogus') } + reset object end it 'allows a method defined with method_missing to be expected' do diff --git a/spec/rspec/mocks/precise_counts_spec.rb b/spec/rspec/mocks/precise_counts_spec.rb index 77227bdb5..276b44028 100644 --- a/spec/rspec/mocks/precise_counts_spec.rb +++ b/spec/rspec/mocks/precise_counts_spec.rb @@ -19,9 +19,9 @@ module Mocks @double.do_something @double.do_something @double.do_something - expect { + expect_fast_failure_from(@double) do @double.do_something - }.to raise_error(RSpec::Mocks::MockExpectationError) + end end it "fails when exactly n times method is never called" do diff --git a/spec/rspec/mocks/reraising_eager_raises_spec.rb b/spec/rspec/mocks/reraising_eager_raises_spec.rb new file mode 100644 index 000000000..dc106d570 --- /dev/null +++ b/spec/rspec/mocks/reraising_eager_raises_spec.rb @@ -0,0 +1,49 @@ +require "spec_helper" + +RSpec.describe "Reraising eager raises during the verify step" do + it "does not reraise when a double receives a message that hasn't been allowed/expected" do + with_unfulfilled_double do |dbl| + expect { dbl.foo }.to fail + expect { verify_all }.not_to raise_error + end + end + + it "reraises when a negative expectation receives a call" do + with_unfulfilled_double do |dbl| + expect(dbl).not_to receive(:foo) + expect { dbl.foo }.to fail + expect { verify_all }.to fail_with(/expected: 0 times with any arguments/) + end + end + + it "reraises when an expectation with a count is exceeded" do + with_unfulfilled_double do |dbl| + expect(dbl).to receive(:foo).exactly(2).times + + dbl.foo + dbl.foo + + expect { dbl.foo }.to fail + expect { verify_all }.to fail_with(/expected: 2 times with any arguments/) + end + end + + it "reraises when an expectation is called with the wrong arguments" do + with_unfulfilled_double do |dbl| + expect(dbl).to receive(:foo).with(1,2,3) + expect { dbl.foo(1,2,4) }.to fail + expect { RSpec::Mocks.verify }.to fail_with(/expected: 1 time with arguments: \(1, 2, 3\)/) + end + end + + it "reraises when an expectation is called out of order", + :pending => "Says bar was called 0 times when it was, see: http://git.io/pjTq" do + with_unfulfilled_double do |dbl| + expect(dbl).to receive(:foo).ordered + expect(dbl).to receive(:bar).ordered + expect { dbl.bar }.to fail + dbl.foo # satisfy the `foo` expectation so that only the bar one fails below + expect { RSpec::Mocks.verify }.to fail_with(/received :bar out of order/) + end + end +end diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index dfe7aa707..61f91ddea 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -192,9 +192,10 @@ def use_rspec_mocks end it 'fails when the message is received' do - dbl = double - dbl.should_not_receive(:foo) - expect { dbl.foo }.to raise_error(RSpec::Mocks::MockExpectationError) + with_unfulfilled_double do |dbl| + dbl.should_not_receive(:foo) + expect { dbl.foo }.to raise_error(RSpec::Mocks::MockExpectationError) + end end it 'does not fail on verification if the message is not received' do @@ -339,7 +340,10 @@ def use_rspec_mocks describe "#should_not_receive" do it "fails if the method is called" do klass.any_instance.should_not_receive(:existing_method) - expect { klass.new.existing_method }.to raise_error(RSpec::Mocks::MockExpectationError) + instance = klass.new + expect_fast_failure_from(instance) do + instance.existing_method + end end it "passes if no method is called" do @@ -354,9 +358,10 @@ def use_rspec_mocks context "with constraints" do it "fails if the method is called with the specified parameters" do klass.any_instance.should_not_receive(:existing_method_with_arguments).with(:argument_one, :argument_two) - expect { - klass.new.existing_method_with_arguments(:argument_one, :argument_two) - }.to raise_error(RSpec::Mocks::MockExpectationError) + instance = klass.new + expect_fast_failure_from(instance) do + instance.existing_method_with_arguments(:argument_one, :argument_two) + end end it "passes if the method is called with different parameters" do diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index 678650e47..816f7e599 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -59,10 +59,10 @@ module Mocks end it "fails if never is specified and the message is called" do - expect { + expect_fast_failure_from(subject, /expected.*0 times/) do ::RSpec::Mocks.expect_message(subject, :foo).never subject.foo - }.to raise_error(/expected.*0 times/) + end end it "sets up basic message expectation, verifies as called" do diff --git a/spec/rspec/mocks/thrice_counts_spec.rb b/spec/rspec/mocks/thrice_counts_spec.rb index 478812b77..82821a1ad 100644 --- a/spec/rspec/mocks/thrice_counts_spec.rb +++ b/spec/rspec/mocks/thrice_counts_spec.rb @@ -28,9 +28,9 @@ module Mocks it "fails fast when call count is higher than expected" do expect(@double).to receive(:do_something).thrice 3.times { @double.do_something } - expect { + expect_fast_failure_from(@double) do @double.do_something - }.to raise_error(RSpec::Mocks::MockExpectationError) + end end it "fails when call count is lower than expected" do diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index 556fcd2ef..ec7b4b204 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -30,9 +30,9 @@ module Mocks expect(@double).to receive(:do_something).twice @double.do_something @double.do_something - expect { + expect_fast_failure_from(@double) do @double.do_something - }.to raise_error(RSpec::Mocks::MockExpectationError) + end end it "fails when call count is lower than expected" do diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index 41f6a92d6..fcc6a6a68 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -72,6 +72,8 @@ module Mocks prevents { expect(o).to receive(:defined_instance_method).with(:a) } + + reset o end it 'checks that stubbed methods are invoked with the correct arity' do @@ -108,6 +110,7 @@ module Mocks expect(o).to receive(:kw_args_method). with(1, 2, 3, hash_including(:required_arg => 1)) } + reset o end it 'does not allow matchers to be used in an actual method call' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2b4f0d6a0..c59beda9c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -43,8 +43,14 @@ def reset_all def with_unfulfilled_double d = double("double") yield d + ensure reset d end + + def expect_fast_failure_from(double, *fail_with_args, &blk) + expect { blk.call(double) }.to fail_with(*fail_with_args) + reset double + end end module VerificationHelpers diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index abaacf7c9..7076b1908 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -4,7 +4,7 @@ def fail raise_error(RSpec::Mocks::MockExpectationError) end - def fail_with(message) + def fail_with(message=nil) raise_error(RSpec::Mocks::MockExpectationError, message) end From fb2e0f1acc5a08f86daf5391dcc98ed8966f145f Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 20 Mar 2015 13:44:34 +0000 Subject: [PATCH 0857/1179] Changelog entry for #884 --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 00a850193..28642735b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -24,6 +24,9 @@ Bug Fixes: error in a situation we can't support) as our implementation has side effects on non-standard objects and there's no solution we could come up with that always works. (Myron Marston, #900) +* Ensure expectations that raise eagerly also raise during RSpec verification. + This means that if exceptions are caught inside test execution the test will + still fail. (Sam Phippen, #884) ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) From b5e514da3af968b07591446b867237ebdea4b9af Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 20 Mar 2015 15:05:09 +0000 Subject: [PATCH 0858/1179] Fix changelog entry for #884 --- Changelog.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 28642735b..50305113e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,12 @@ Enhancements: * Verifying doubles failure messages now distinguish between class and instance level methods. (Tim Wade, #896, #908) +Bug Fixes: + +* Ensure expectations that raise eagerly also raise during RSpec verification. + This means that if exceptions are caught inside test execution the test will + still fail. (Sam Phippen, #884) + ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) @@ -24,9 +30,6 @@ Bug Fixes: error in a situation we can't support) as our implementation has side effects on non-standard objects and there's no solution we could come up with that always works. (Myron Marston, #900) -* Ensure expectations that raise eagerly also raise during RSpec verification. - This means that if exceptions are caught inside test execution the test will - still fail. (Sam Phippen, #884) ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) From b674f2881bae9e6294bf9168820b5110282b0e4e Mon Sep 17 00:00:00 2001 From: John Ceh Date: Tue, 24 Mar 2015 00:53:01 -0700 Subject: [PATCH 0859/1179] Small fix for typo --- features/verifying_doubles/dynamic_classes.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index e7b52fc9f..eaf9cf3f1 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -101,7 +101,7 @@ Feature: Dynamic classes # # or you can use: # - # RSpec::Mocks.configuration.when_declaring_verifying_double do |refernece| + # RSpec::Mocks.configuration.when_declaring_verifying_double do |reference| # reference.target.define_attribute_methods # end """ From e575da3a821c80fe6e5b2105d5ae0cbc4348c7a7 Mon Sep 17 00:00:00 2001 From: John Ceh Date: Sun, 29 Mar 2015 04:32:15 -0700 Subject: [PATCH 0860/1179] Fixes issue 915 - displaying invalid counts instead of irrelevant arguments --- lib/rspec/mocks/error_generator.rb | 58 ++++++---- lib/rspec/mocks/proxy.rb | 16 ++- .../mocks/matchers/have_received_spec.rb | 103 +++++++++++++++++- .../stubbed_message_expectations_spec.rb | 20 +++- 4 files changed, 166 insertions(+), 31 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 86622159c..ada23d7fc 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -50,36 +50,35 @@ def raise_unexpected_message_error(message, *args) end # @private - def raise_unexpected_message_args_error(expectation, *args) + def message_error(expectation, *args) expected_args = format_args(*expectation.expected_args) actual_args = format_received_args(*args) - diff = diff_message(expectation.expected_args, args) - message = default_error_message(expectation, expected_args, actual_args) - message << "\nDiff:#{diff}" unless diff.empty? - __raise message + if single_message?(args) + diff = diff_message(expectation.expected_args, *args) + message << "\nDiff:#{diff}" unless diff.empty? + end + + message end # @private - def raise_missing_default_stub_error(expectation, *args) - expected_args = format_args(*expectation.expected_args) - actual_args = format_received_args(*args) - diff = diff_message(expectation.expected_args, args) + def raise_unexpected_message_args_error(expectation, *args) + __raise message_error(expectation, *args) + end - message = default_error_message(expectation, expected_args, actual_args) - message << "\nDiff:\n #{diff}" unless diff.empty? + # @private + def raise_missing_default_stub_error(expectation, *args) + message = message_error(expectation, *args) message << "\n Please stub a default value first if message might be received with other args as well. \n" __raise message end # @private - def raise_similar_message_args_error(expectation, *args_for_multiple_calls) - expected_args = format_args(*expectation.expected_args) - actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ") - - __raise(default_error_message(expectation, expected_args, actual_args)) + def raise_similar_message_args_error(expectation, *args) + __raise message_error(expectation, *args) end def default_error_message(expectation, expected_args, actual_args) @@ -288,11 +287,12 @@ def arg_has_valid_description?(arg) end def format_received_args(*args) - args.empty? ? "(no args)" : "(" + received_arg_list(*args) + ")" - end - - def received_arg_list(*args) - args.map(&:inspect).join(", ") + return "(no args)" if args.empty? || args.first.empty? + return formatted_single_message(args) if single_message?(args) + grouped_list = grouped_args(args).map do |arg, index| + [formatted_single_message([arg]), group_count(index, args)].join + end + grouped_list.join("\n ") end def count_message(count, expectation_count_type=nil) @@ -304,6 +304,22 @@ def count_message(count, expectation_count_type=nil) def times(count) "#{count} time#{count == 1 ? '' : 's'}" end + + def formatted_single_message(args) + "(" + args.first.map(&:inspect).join(", ") + ")" + end + + def grouped_args(args) + Hash[args.group_by { |x| x }.map { |k, v| [k, v.count] }] + end + + def single_message?(args) + args.size == 1 + end + + def group_count(index, args) + args.size > 1 || index > 1 ? ' (' + times(index) + ')' : '' + end end end end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index b936a3a62..2cdb0ae84 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -98,11 +98,15 @@ def replay_received_message_on(expectation, &block) # @private def check_for_unexpected_arguments(expectation) - @messages_received.each do |(method_name, args, _)| - next unless expectation.matches_name_but_not_args(method_name, *args) + return if @messages_received.empty? - raise_unexpected_message_args_error(expectation, *args) - end + # Check if any of the arguments were expected. + return if @messages_received.any? do |(method_name, args, _)| + !expectation.matches_name_but_not_args(method_name, *args) + end + unexpected_args = @messages_received.map { |_, args, _| args } + + raise_unexpected_message_args_error(expectation, *unexpected_args) end # @private @@ -171,11 +175,11 @@ def message_received(message, *args, &block) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? if null_object? || !has_negative_expectation?(message) - raise_unexpected_message_args_error(expectation, *args) + raise_unexpected_message_args_error(expectation, args) end elsif (stub = find_almost_matching_stub(message, *args)) stub.advise(*args) - raise_missing_default_stub_error(stub, *args) + raise_missing_default_stub_error(stub, args) elsif Class === @object @object.superclass.__send__(message, *args, &block) else diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 7bda18a3f..d2dc71cab 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -141,9 +141,106 @@ module Mocks let(:the_dbl) { double(:expected_method => nil) } before do - the_dbl.expected_method - the_dbl.expected_method - the_dbl.expected_method + the_dbl.expected_method(:one) + the_dbl.expected_method(:two) + the_dbl.expected_method(:one) + end + + context "when constrained by `with`" do + it 'only considers the calls with matching args' do + expect(the_dbl).to have_received(:expected_method).with(:one).twice + expect(the_dbl).to have_received(:expected_method).with(:two).once + end + + context "when the message is received without any args matching" do + it 'includes unmatched args in the error message' do + expect { + expect(the_dbl).to have_received(:expected_method).with(:three).once + }.to raise_error(Expectations::ExpectationNotMetError, + a_string_including("expected: (:three)", + "got:", "(:one) (2 times)", + "(:two) (1 time)")) + end + end + + context "when the message is received too many times" do + it 'includes the counts of calls with matching args in the error message' do + expect { + expect(the_dbl).to have_received(:expected_method).with(:one).once + }.to raise_error(Expectations::ExpectationNotMetError, + a_string_including("expected: 1 time", + "received: 2 times")) + end + end + + context "when the message is received too few times" do + it 'includes the counts of calls with matching args in the error message' do + expect { + expect(the_dbl).to have_received(:expected_method).with(:two).twice + }.to raise_error(Expectations::ExpectationNotMetError, + a_string_including("expected: 2 times", + "received: 1 time")) + end + end + + context "when contrained with grouped arguments `with`" do + it 'groups the "got" arguments based on the method call that included them' do + dbl = double(:expected_method => nil) + dbl.expected_method(:one, :four) + dbl.expected_method(:two, :four) + dbl.expected_method(:three, :four) + dbl.expected_method(:one, :four) + dbl.expected_method(:three, :four) + dbl.expected_method(:three, :four) + + expect { + expect(dbl).to have_received(:expected_method).with(:four, :four).once + }.to raise_error( + Expectations::ExpectationNotMetError, + a_string_including("expected: (:four, :four)", + "got:","(:one, :four) (2 times)", + "(:two, :four) (1 time)", + "(:three, :four) (3 times)")) + end + + it 'includes single arguments based on the method call that included them' do + dbl = double(:expected_method => nil) + dbl.expected_method(:one, :four) + + expect { + expect(dbl).to have_received(:expected_method).with(:three, :four).once + }.to raise_error( + Expectations::ExpectationNotMetError, + a_string_including("expected: (:three, :four)", + "got: (:one, :four)")) + end + + it 'keeps the array combinations distinguished in the group' do + dbl = double(:expected_method => nil) + dbl.expected_method([:one], :four) + dbl.expected_method(:one, [:four]) + + expect { + expect(dbl).to have_received(:expected_method).with(:one, :four).once + }.to raise_error( + Expectations::ExpectationNotMetError, + a_string_including("expected: (:one, :four)", + "got:","([:one], :four)", + "(:one, [:four])")) + end + + it 'does not group counts on repeated arguments for a single message' do + dbl = double(:expected_method => nil) + dbl.expected_method(:one, :one, :two) + + expect { + expect(dbl).to have_received(:expected_method).with(:one, :two, :three).once + }.to raise_error( + Expectations::ExpectationNotMetError, + a_string_including("expected: (:one, :two, :three)", + "got:","(:one, :one, :two)")) + end + end end context "exactly" do diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index 22222bc86..b3021aff0 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -13,7 +13,13 @@ dbl.foo('third') expect { verify dbl - }.to raise_error(%Q|Double "double" received :foo with unexpected arguments\n expected: ("first")\n got: ("second"), ("third")|) + }.to raise_error( + RSpec::Mocks::MockExpectationError, + a_string_including( + %Q|Double "double" received :foo with unexpected arguments|, + "expected: (\"first\")", + "got:","(\"second\")", + "(\"third\")")) reset dbl end @@ -52,5 +58,17 @@ expect(e.message).to include('expected: ("a", ["b"])', 'got: (["a"], "b")') } end + + it 'distinguishes between duplicate individual values and arrays properly' do + dbl = double + allow(dbl).to receive(:foo).with('a', ['b'], 'b') + + expect { + dbl.foo(['a'], 'b', 'b') + }.to raise_error { |e| + expect(e.message).to include('expected: ("a", ["b"], "b")', 'got: (["a"], "b", "b")') + } + end + end end From 3f72f4ce998559811abc97ea2db698b9d857c61f Mon Sep 17 00:00:00 2001 From: John Ceh Date: Sun, 5 Apr 2015 03:21:26 -0700 Subject: [PATCH 0861/1179] Fixes Issue 915 - Second part fix for error message to include counts for expect-to-receive error messages --- lib/rspec/mocks/error_generator.rb | 56 ++++++++++---------------- lib/rspec/mocks/message_expectation.rb | 7 ++++ lib/rspec/mocks/proxy.rb | 9 ++++- spec/rspec/mocks/once_counts_spec.rb | 23 +++++++++++ spec/rspec/mocks/twice_counts_spec.rb | 17 ++++++++ 5 files changed, 76 insertions(+), 36 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index ada23d7fc..74ed9438b 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -50,35 +50,21 @@ def raise_unexpected_message_error(message, *args) end # @private - def message_error(expectation, *args) - expected_args = format_args(*expectation.expected_args) - actual_args = format_received_args(*args) - message = default_error_message(expectation, expected_args, actual_args) - - if single_message?(args) - diff = diff_message(expectation.expected_args, *args) - message << "\nDiff:#{diff}" unless diff.empty? - end - - message - end - - # @private - def raise_unexpected_message_args_error(expectation, *args) - __raise message_error(expectation, *args) + def raise_unexpected_message_args_error(expectation, *args_for_multiple_calls) + __raise error_message(expectation, *args_for_multiple_calls) end # @private def raise_missing_default_stub_error(expectation, *args) - message = message_error(expectation, *args) + message = error_message(expectation, *args) message << "\n Please stub a default value first if message might be received with other args as well. \n" __raise message end # @private - def raise_similar_message_args_error(expectation, *args) - __raise message_error(expectation, *args) + def raise_similar_message_args_error(expectation, *args_for_multiple_calls) + __raise error_message(expectation, *args_for_multiple_calls) end def default_error_message(expectation, expected_args, actual_args) @@ -225,6 +211,19 @@ def unexpected_arguments_message(expected_args_string, actual_args_string) "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}" end + def error_message(expectation, *args_for_multiple_calls) + expected_args = format_args(*expectation.expected_args) + actual_args = format_received_args(*args_for_multiple_calls) + message = default_error_message(expectation, expected_args, actual_args) + + if args_for_multiple_calls.one? + diff = diff_message(expectation.expected_args, *args_for_multiple_calls) + message << "\nDiff:#{diff}" unless diff.empty? + end + + message + end + def diff_message(expected_args, actual_args) formatted_expected_args = expected_args.map do |x| RSpec::Support.rspec_description_for_object(x) @@ -286,13 +285,10 @@ def arg_has_valid_description?(arg) RSpec::Support.is_a_matcher?(arg) && arg.respond_to?(:description) end - def format_received_args(*args) - return "(no args)" if args.empty? || args.first.empty? - return formatted_single_message(args) if single_message?(args) - grouped_list = grouped_args(args).map do |arg, index| - [formatted_single_message([arg]), group_count(index, args)].join - end - grouped_list.join("\n ") + def format_received_args(*args_for_multiple_calls) + grouped_args(args_for_multiple_calls).map do |args_for_one_call, index| + [format_args(*args_for_one_call), group_count(index, args_for_multiple_calls)].join + end.join("\n ") end def count_message(count, expectation_count_type=nil) @@ -305,18 +301,10 @@ def times(count) "#{count} time#{count == 1 ? '' : 's'}" end - def formatted_single_message(args) - "(" + args.first.map(&:inspect).join(", ") + ")" - end - def grouped_args(args) Hash[args.group_by { |x| x }.map { |k, v| [k, v.count] }] end - def single_message?(args) - args.size == 1 - end - def group_count(index, args) args.size > 1 || index > 1 ? ' (' + times(index) + ')' : '' end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index f7a5edc23..fb2a0b0ae 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -32,6 +32,9 @@ def verify_messages_received end end end + + def unadvise(_) + end end # Represents an individual method stub or message expectation. The methods @@ -459,6 +462,10 @@ def advise(*args) similar_messages << args end + def unadvise(args) + similar_messages.delete_if { |message| args.include?(message) } + end + def generate_error if similar_messages.empty? @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 2cdb0ae84..2f5720fe0 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -104,9 +104,8 @@ def check_for_unexpected_arguments(expectation) return if @messages_received.any? do |(method_name, args, _)| !expectation.matches_name_but_not_args(method_name, *args) end - unexpected_args = @messages_received.map { |_, args, _| args } - raise_unexpected_message_args_error(expectation, *unexpected_args) + raise_unexpected_message_args_error(expectation, *messages_arg_list) end # @private @@ -145,6 +144,11 @@ def received_message?(method_name, *args, &block) @messages_received.any? { |array| array == [method_name, args, block] } end + # @private + def messages_arg_list + @messages_received.map { |_, args, _| args } + end + # @private def has_negative_expectation?(message) method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) } @@ -170,6 +174,7 @@ def message_received(message, *args, &block) end stub.invoke(nil, *args, &block) elsif expectation + expectation.unadvise(messages_arg_list) expectation.invoke(stub, *args, &block) elsif (expectation = find_almost_matching_expectation(message, *args)) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? diff --git a/spec/rspec/mocks/once_counts_spec.rb b/spec/rspec/mocks/once_counts_spec.rb index eb368b44d..86ca70b35 100644 --- a/spec/rspec/mocks/once_counts_spec.rb +++ b/spec/rspec/mocks/once_counts_spec.rb @@ -45,6 +45,29 @@ module Mocks verify @double }.to raise_error(RSpec::Mocks::MockExpectationError) end + + context "when called with the wrong number of times with the specified args and also called with different args" do + it "mentions the wrong call count in the failure message rather than the different args" do + allow(@double).to receive(:do_something) # allow any args... + expect(@double).to receive(:do_something).with(:args, 1).once + + @double.do_something(:args, 2) + @double.do_something(:args, 1) + + expect { + # we've grouped these lines because it should probably fail fast + # on the first line (since our expectation above only allows one + # call with these args), but currently it fails with a confusing + # message on verification, and ultimately we care more about + # what the message is than when it is raised. Still, it would be + # preferrable for the error to be triggered on the first line, + # so it'd be good to update this spec to enforce that once we + # get the failure message right. + @double.do_something(:args, 1) + verify @double + }.to fail_with(a_string_including("expected: 1 time", "received: 2 times")) + end + end end end end diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index ec7b4b204..9c6e253e9 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -59,6 +59,23 @@ module Mocks }.to raise_error(RSpec::Mocks::MockExpectationError) reset @double end + + context "when called with the wrong number of times with the specified args and also called with different args" do + it "mentions the wrong call count in the failure message rather than the different args" do + allow(@double).to receive(:do_something) # allow any args... + expect(@double).to receive(:do_something).with(:args, 1).twice + + @double.do_something(:args, 2) + @double.do_something(:args, 1) + @double.do_something(:args, 2) + @double.do_something(:args, 1) + + expect { + @double.do_something(:args, 1) + verify @double + }.to fail_with(a_string_including("expected: 2 times", "received: 3 times")) + end + end end end end From 97703a8fae477c7bd0c9f0d734929ea30a29bef2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 11:20:20 -0700 Subject: [PATCH 0862/1179] Add changelog entries for #918. --- Changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Changelog.md b/Changelog.md index 50305113e..30b78fe72 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,12 +11,21 @@ Enhancements: * Avoid loading `stringio` unnecessarily. (Myron Marston, #894) * Verifying doubles failure messages now distinguish between class and instance level methods. (Tim Wade, #896, #908) +* Improve mock expectation failure messages so that it combines both + number of times and the received arguments in the output. (John Ceh, #918) Bug Fixes: * Ensure expectations that raise eagerly also raise during RSpec verification. This means that if exceptions are caught inside test execution the test will still fail. (Sam Phippen, #884) +* Fix `have_received(msg).with(args).exactly(n).times` and + `receive(msg).with(args).exactly(n).times` failure messages + for when the message was received the wrong number of times with + the specified args, and also received additional times with other + arguments. Previously it confusingly listed the arguments as being + mis-matched (even when the double was allowed to receive with any + args) rather than listing the count. (John Ceh, #918) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) From 6d8b31b40231948d2bbb30d90e525efed8b8c99f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 11:22:51 -0700 Subject: [PATCH 0863/1179] Reduce object allocations in error generator. `' (' + string + ')'` allocates 4 strings: * `' ('` * The string returned by `' (' + string` (let's call it `str2`) * `')'` * The string returned by `str2 + ')'` In contrast, `" (#{times(index)})"` only allocates one string. Likewise, `[str1, str2].join` allocates an array only to join it, where as `"#{str1}#{str2}"` avoids the array allocation. --- lib/rspec/mocks/error_generator.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 74ed9438b..dc1cfb081 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -274,7 +274,8 @@ def arg_message(*args) end def format_args(*args) - args.empty? ? "(no args)" : "(" + arg_list(*args) + ")" + return "(no args)" if args.empty? + "(#{arg_list(*args)})" end def arg_list(*args) @@ -287,7 +288,7 @@ def arg_has_valid_description?(arg) def format_received_args(*args_for_multiple_calls) grouped_args(args_for_multiple_calls).map do |args_for_one_call, index| - [format_args(*args_for_one_call), group_count(index, args_for_multiple_calls)].join + "#{format_args(*args_for_one_call)}#{group_count(index, args_for_multiple_calls)}" end.join("\n ") end @@ -306,7 +307,7 @@ def grouped_args(args) end def group_count(index, args) - args.size > 1 || index > 1 ? ' (' + times(index) + ')' : '' + " (#{times(index)})" if args.size > 1 || index > 1 end end end From 31580689aba4f80c33e6bd1295058b999343afee Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 11:31:59 -0700 Subject: [PATCH 0864/1179] Standardize fail matchers. This mirrors an earlier change in rspec-expectations: https://github.com/rspec/rspec-expectations/commit/b4b4fbbf1e047d0871c68da572e6cc262ec0afec --- spec/rspec/mocks/argument_matchers_spec.rb | 44 +++++++++---------- spec/rspec/mocks/spy_spec.rb | 4 +- ...class_double_with_class_not_loaded_spec.rb | 2 +- ...tance_double_with_class_not_loaded_spec.rb | 2 +- spec/support/matchers.rb | 11 +++-- 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index 86b912227..2f113a7a9 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -22,7 +22,7 @@ module Mocks expect(a_double).to receive(:random_call).with(boolean) expect { a_double.random_call("false") - }.to fail_matching "expected: (boolean)" + }.to fail_including "expected: (boolean)" end end @@ -41,7 +41,7 @@ module Mocks expect(a_double).to receive(:random_call).with(kind_of(Numeric)) expect { a_double.random_call(true) - }.to fail_matching "expected: (kind of Numeric)" + }.to fail_including "expected: (kind of Numeric)" end it "matches arguments that have defined `kind_of?` to return true" do @@ -88,7 +88,7 @@ module Mocks expect(a_double).to receive(:random_call).with(instance_of(Numeric)) expect { a_double.random_call(1.5) - }.to fail_matching "expected: (an_instance_of(Numeric))" + }.to fail_including "expected: (an_instance_of(Numeric))" end end @@ -105,7 +105,7 @@ module Mocks it "handles non matching instances nicely", :reset => true do expect(a_double).to receive(:random_call).with(anything) - expect { a_double.random_call }.to fail_matching "expected: (anything)" + expect { a_double.random_call }.to fail_including "expected: (anything)" end end @@ -124,7 +124,7 @@ module Mocks expect(a_double).to receive(:random_call).with(duck_type(:abs, :div)) expect { a_double.random_call("I don't respond to :abs or :div") - }.to fail_matching "expected: (duck_type(:abs, :div))" + }.to fail_including "expected: (duck_type(:abs, :div))" end end @@ -161,11 +161,11 @@ module Mocks end it "does not match a call where the first two args do not match", :reset => true do - expect { a_double.random_call(1, "bar", 2, 3) }.to fail_matching "expected: (1, /foo/, *(any args))" + expect { a_double.random_call(1, "bar", 2, 3) }.to fail_including "expected: (1, /foo/, *(any args))" end it "does not match a call of no args", :reset => true do - expect { a_double.random_call }.to fail_matching "expected: (1, /foo/, *(any args))" + expect { a_double.random_call }.to fail_including "expected: (1, /foo/, *(any args))" end end @@ -185,11 +185,11 @@ module Mocks end it "does not match a call where the last two args do not match", :reset => true do - expect { a_double.random_call(1, "bar", 2, 3) }.to fail_matching "expected: (*(any args), 1, /foo/)" + expect { a_double.random_call(1, "bar", 2, 3) }.to fail_including "expected: (*(any args), 1, /foo/)" end it "does not match a call of no args", :reset => true do - expect { a_double.random_call }.to fail_matching "expected: (*(any args), 1, /foo/)" + expect { a_double.random_call }.to fail_including "expected: (*(any args), 1, /foo/)" end end @@ -209,11 +209,11 @@ module Mocks end it "does not match a call where the first and last args do not match", :reset => true do - expect { a_double.random_call(nil, "bar", 2, 3) }.to fail_matching "expected: (1, *(any args), /foo/)" + expect { a_double.random_call(nil, "bar", 2, 3) }.to fail_including "expected: (1, *(any args), /foo/)" end it "does not match a call of no args", :reset => true do - expect { a_double.random_call }.to fail_matching "expected: (1, *(any args), /foo/)" + expect { a_double.random_call }.to fail_including "expected: (1, *(any args), /foo/)" end end @@ -234,7 +234,7 @@ module Mocks it "fails no_args with one arg", :reset => true do expect(a_double).to receive(:msg).with(no_args) - expect { a_double.msg(37) }.to fail_matching "expected: (no args)" + expect { a_double.msg(37) }.to fail_including "expected: (no args)" end context "when passed with other arguments" do @@ -256,7 +256,7 @@ module Mocks expect(a_double).to receive(:random_call).with(hash_including(:a => 1)) expect { a_double.random_call(:a => 2) - }.to fail_matching "expected: (hash_including(:a=>1))" + }.to fail_including "expected: (hash_including(:a=>1))" end end @@ -270,7 +270,7 @@ module Mocks expect(a_double).to receive(:random_call).with(hash_excluding(:a => 1)) expect { a_double.random_call(:a => 1) - }.to fail_matching "expected: (hash_not_including(:a=>1))" + }.to fail_including "expected: (hash_not_including(:a=>1))" end end @@ -284,14 +284,14 @@ module Mocks expect(a_double).to receive(:msg).with(array_including(1, 2, 3)) expect { a_double.msg(1, 2, 3) - }.to fail_matching "expected: (array_including(1, 2, 3))" + }.to fail_including "expected: (array_including(1, 2, 3))" end it "fails array_including when arg doesn't contain all elements", :reset => true do expect(a_double).to receive(:msg).with(array_including(1, 2, 3)) expect { a_double.msg([1, 2]) - }.to fail_matching "expected: (array_including(1, 2, 3))" + }.to fail_including "expected: (array_including(1, 2, 3))" end end @@ -308,7 +308,7 @@ module Mocks expect(a_double).to receive(:msg).with(equal(3)) # This spec is generating warnings on 1.8.7, not sure why so # this does with_isolated_stderr to kill them. @samphippen 3rd Jan 2013. - expect { with_isolated_stderr { a_double.msg(37) } }.to fail_matching "expected: (equal 3)" + expect { with_isolated_stderr { a_double.msg(37) } }.to fail_including "expected: (equal 3)" end it "fails when given an arbitrary object that returns false from #===", :reset => true do @@ -363,14 +363,14 @@ module Mocks expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") expect do a_double.random_call(:a => "b", :c => "e") - end.to fail_matching(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/) + end.to fail_with(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/) end it "fails for a hash w/ wrong keys", :reset => true do expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") expect do a_double.random_call("a" => "b", "c" => "d") - end.to fail_matching(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/) + end.to fail_with(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/) end it "matches a class against itself" do @@ -403,21 +403,21 @@ module Mocks arg = double(:description => nil, :inspect => "my_thing") expect(a_double).to receive(:msg).with(3) - expect { a_double.msg arg }.to fail_matching "got: (my_thing)" + expect { a_double.msg arg }.to fail_including "got: (my_thing)" end it "fails with sensible message when arg#description is nil", :reset => true do arg = double(:description => nil, :inspect => "my_thing") expect(a_double).to receive(:msg).with(arg) - expect { a_double.msg 3 }.to fail_matching "expected: (my_thing)" + expect { a_double.msg 3 }.to fail_including "expected: (my_thing)" end it "fails with sensible message when arg#description is blank", :reset => true do arg = double(:description => "", :inspect => "my_thing") expect(a_double).to receive(:msg).with(arg) - expect { a_double.msg 3 }.to fail_matching "expected: (my_thing)" + expect { a_double.msg 3 }.to fail_including "expected: (my_thing)" end end end diff --git a/spec/rspec/mocks/spy_spec.rb b/spec/rspec/mocks/spy_spec.rb index 57868a234..0921e506c 100644 --- a/spec/rspec/mocks/spy_spec.rb +++ b/spec/rspec/mocks/spy_spec.rb @@ -39,13 +39,13 @@ it 'fails fast when `have_received` is passed an undefined method name' do expect { expect(subject).to have_received(:bar) - }.to fail_matching("does not implement") + }.to fail_including("does not implement") end it 'fails fast when negative `have_received` is passed an undefined method name' do expect { expect(subject).to_not have_received(:bar) - }.to fail_matching("does not implement") + }.to fail_including("does not implement") end end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb index c551719d6..ac499681a 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb @@ -13,7 +13,7 @@ module Mocks o = class_double("NonLoadedClass") expect { o.foo - }.to fail_matching('Double "NonLoadedClass"') + }.to fail_including('Double "NonLoadedClass"') end it 'allows any method to be stubbed' do diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb index 32b97b19d..f3ef3c3c5 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb @@ -13,7 +13,7 @@ module Mocks o = instance_double("NonLoadedClass") expect { o.foo - }.to fail_matching('Double "NonLoadedClass (instance)"') + }.to fail_including('Double "NonLoadedClass (instance)"') end it 'allows any instance method to be stubbed' do diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 7076b1908..ec967a445 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -4,12 +4,15 @@ def fail raise_error(RSpec::Mocks::MockExpectationError) end - def fail_with(message=nil) - raise_error(RSpec::Mocks::MockExpectationError, message) + def fail_with(*args) + raise_error(RSpec::Mocks::MockExpectationError, *args) end - def fail_matching(message) - raise_error(RSpec::Mocks::MockExpectationError, (String === message ? /#{Regexp.escape(message)}/ : message)) + def fail_including(*snippets) + raise_error( + RSpec::Mocks::MockExpectationError, + a_string_including(*snippets) + ) end end end From 4ef378f796754813ad5e366b72421ef96646d98a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 11:37:00 -0700 Subject: [PATCH 0865/1179] Leverage `fail_including` for shorter matcher expressions. --- .../mocks/matchers/have_received_spec.rb | 49 ++++++++----------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index d2dc71cab..93202a88a 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -156,10 +156,9 @@ module Mocks it 'includes unmatched args in the error message' do expect { expect(the_dbl).to have_received(:expected_method).with(:three).once - }.to raise_error(Expectations::ExpectationNotMetError, - a_string_including("expected: (:three)", - "got:", "(:one) (2 times)", - "(:two) (1 time)")) + }.to fail_including("expected: (:three)", + "got:", "(:one) (2 times)", + "(:two) (1 time)") end end @@ -167,9 +166,7 @@ module Mocks it 'includes the counts of calls with matching args in the error message' do expect { expect(the_dbl).to have_received(:expected_method).with(:one).once - }.to raise_error(Expectations::ExpectationNotMetError, - a_string_including("expected: 1 time", - "received: 2 times")) + }.to fail_including("expected: 1 time", "received: 2 times") end end @@ -177,9 +174,7 @@ module Mocks it 'includes the counts of calls with matching args in the error message' do expect { expect(the_dbl).to have_received(:expected_method).with(:two).twice - }.to raise_error(Expectations::ExpectationNotMetError, - a_string_including("expected: 2 times", - "received: 1 time")) + }.to fail_including("expected: 2 times", "received: 1 time") end end @@ -195,12 +190,10 @@ module Mocks expect { expect(dbl).to have_received(:expected_method).with(:four, :four).once - }.to raise_error( - Expectations::ExpectationNotMetError, - a_string_including("expected: (:four, :four)", - "got:","(:one, :four) (2 times)", - "(:two, :four) (1 time)", - "(:three, :four) (3 times)")) + }.to fail_including("expected: (:four, :four)", + "got:","(:one, :four) (2 times)", + "(:two, :four) (1 time)", + "(:three, :four) (3 times)") end it 'includes single arguments based on the method call that included them' do @@ -209,10 +202,7 @@ module Mocks expect { expect(dbl).to have_received(:expected_method).with(:three, :four).once - }.to raise_error( - Expectations::ExpectationNotMetError, - a_string_including("expected: (:three, :four)", - "got: (:one, :four)")) + }.to fail_including("expected: (:three, :four)", "got: (:one, :four)") end it 'keeps the array combinations distinguished in the group' do @@ -222,11 +212,9 @@ module Mocks expect { expect(dbl).to have_received(:expected_method).with(:one, :four).once - }.to raise_error( - Expectations::ExpectationNotMetError, - a_string_including("expected: (:one, :four)", - "got:","([:one], :four)", - "(:one, [:four])")) + }.to fail_including("expected: (:one, :four)", + "got:","([:one], :four)", + "(:one, [:four])") end it 'does not group counts on repeated arguments for a single message' do @@ -235,10 +223,7 @@ module Mocks expect { expect(dbl).to have_received(:expected_method).with(:one, :two, :three).once - }.to raise_error( - Expectations::ExpectationNotMetError, - a_string_including("expected: (:one, :two, :three)", - "got:","(:one, :one, :two)")) + }.to fail_including("expected: (:one, :two, :three)", "got:","(:one, :one, :two)") end end end @@ -605,6 +590,12 @@ def meet_expectation(double, method_name, *args) def double_with_unmet_expectation(method_name) double('double', method_name => true) end + + # Override `fail_including` for this context, since `have_received` is a normal + # rspec-expectations matcher, the error class is different. + def fail_including(*snippets) + raise_error(RSpec::Expectations::ExpectationNotMetError, a_string_including(*snippets)) + end end end end From 3d6bfa1fb02f8e7a1e91871df8f16d1c73a9cb93 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 11:37:40 -0700 Subject: [PATCH 0866/1179] Move comment over from `once_counts_spec.rb`. It applies here, too, and I hope we'll address it at some point. --- spec/rspec/mocks/twice_counts_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index 9c6e253e9..e8365076a 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -71,6 +71,14 @@ module Mocks @double.do_something(:args, 1) expect { + # we've grouped these lines because it should probably fail fast + # on the first line (since our expectation above only allows one + # call with these args), but currently it fails with a confusing + # message on verification, and ultimately we care more about + # what the message is than when it is raised. Still, it would be + # preferrable for the error to be triggered on the first line, + # so it'd be good to update this spec to enforce that once we + # get the failure message right. @double.do_something(:args, 1) verify @double }.to fail_with(a_string_including("expected: 2 times", "received: 3 times")) From a1027d24696a4b1ebdf37c1d908de204dce89e6f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 11:41:27 -0700 Subject: [PATCH 0867/1179] Move away from using arg splats for error generation. We were splatting and resplatting through the error generation process, but it was a bit confusing. I suspect it performs worse than just passing the same array around anyway. One concrete improvement from this is that we had some places where we were passing the args from a single method call to an method accepting a splat of args_from_multiple_calls. Wrapping the single-call in an array makes the conversion more explicit. --- lib/rspec/mocks/error_generator.rb | 56 +++++++++++++------------- lib/rspec/mocks/message_expectation.rb | 10 ++--- lib/rspec/mocks/proxy.rb | 18 ++++----- lib/rspec/mocks/test_double.rb | 2 +- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index dc1cfb081..772d21cc5 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -45,26 +45,26 @@ def opts end # @private - def raise_unexpected_message_error(message, *args) - __raise "#{intro} received unexpected message :#{message}#{arg_message(*args)}" + def raise_unexpected_message_error(message, args) + __raise "#{intro} received unexpected message :#{message}#{arg_message(args)}" end # @private - def raise_unexpected_message_args_error(expectation, *args_for_multiple_calls) - __raise error_message(expectation, *args_for_multiple_calls) + def raise_unexpected_message_args_error(expectation, args_for_multiple_calls) + __raise error_message(expectation, args_for_multiple_calls) end # @private - def raise_missing_default_stub_error(expectation, *args) - message = error_message(expectation, *args) + def raise_missing_default_stub_error(expectation, args) + message = error_message(expectation, args) message << "\n Please stub a default value first if message might be received with other args as well. \n" __raise message end # @private - def raise_similar_message_args_error(expectation, *args_for_multiple_calls) - __raise error_message(expectation, *args_for_multiple_calls) + def raise_similar_message_args_error(expectation, args_for_multiple_calls) + __raise error_message(expectation, args_for_multiple_calls) end def default_error_message(expectation, expected_args, actual_args) @@ -78,10 +78,10 @@ def default_error_message(expectation, expected_args, actual_args) # rubocop:disable Style/ParameterLists # @private - def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, *args) + def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, args) expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) - received_part = received_part_of_expectation_error(actual_received_count, *args) - __raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}" + received_part = received_part_of_expectation_error(actual_received_count, args) + __raise "(#{intro}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}" end # rubocop:enable Style/ParameterLists @@ -131,7 +131,7 @@ def raise_expired_test_double_error end # @private - def received_part_of_expectation_error(actual_received_count, *args) + def received_part_of_expectation_error(actual_received_count, args) "received: #{count_message(actual_received_count)}" + method_call_args_description(args) do actual_received_count > 0 && args.length > 0 @@ -153,7 +153,7 @@ def method_call_args_description(args, &block) when ArgumentMatchers::NoArgsMatcher then " with no arguments" else if block.call - " with arguments: #{format_args(*args)}" + " with arguments: #{format_args(args)}" else "" end @@ -161,8 +161,8 @@ def method_call_args_description(args, &block) end # @private - def describe_expectation(verb, message, expected_received_count, _actual_received_count, *args) - "#{verb} #{message}#{format_args(*args)} #{count_message(expected_received_count)}" + def describe_expectation(verb, message, expected_received_count, _actual_received_count, args) + "#{verb} #{message}#{format_args(args)} #{count_message(expected_received_count)}" end # @private @@ -172,12 +172,12 @@ def raise_out_of_order_error(message) # @private def raise_missing_block_error(args_to_yield) - __raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed" + __raise "#{intro} asked to yield |#{arg_list(args_to_yield)}| but no block was passed" end # @private def raise_wrong_arity_error(args_to_yield, signature) - __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with #{signature.description}" + __raise "#{intro} yielded |#{arg_list(args_to_yield)}| to block with #{signature.description}" end # @private @@ -211,13 +211,13 @@ def unexpected_arguments_message(expected_args_string, actual_args_string) "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}" end - def error_message(expectation, *args_for_multiple_calls) - expected_args = format_args(*expectation.expected_args) - actual_args = format_received_args(*args_for_multiple_calls) + def error_message(expectation, args_for_multiple_calls) + expected_args = format_args(expectation.expected_args) + actual_args = format_received_args(args_for_multiple_calls) message = default_error_message(expectation, expected_args, actual_args) if args_for_multiple_calls.one? - diff = diff_message(expectation.expected_args, *args_for_multiple_calls) + diff = diff_message(expectation.expected_args, args_for_multiple_calls.first) message << "\nDiff:#{diff}" unless diff.empty? end @@ -269,16 +269,16 @@ def __raise(message) Kernel.raise(RSpec::Mocks::MockExpectationError, message) end - def arg_message(*args) - " with " + format_args(*args) + def arg_message(args) + " with " + format_args(args) end - def format_args(*args) + def format_args(args) return "(no args)" if args.empty? - "(#{arg_list(*args)})" + "(#{arg_list(args)})" end - def arg_list(*args) + def arg_list(args) args.map { |arg| arg_has_valid_description?(arg) ? arg.description : arg.inspect }.join(", ") end @@ -286,9 +286,9 @@ def arg_has_valid_description?(arg) RSpec::Support.is_a_matcher?(arg) && arg.respond_to?(:description) end - def format_received_args(*args_for_multiple_calls) + def format_received_args(args_for_multiple_calls) grouped_args(args_for_multiple_calls).map do |args_for_one_call, index| - "#{format_args(*args_for_one_call)}#{group_count(index, args_for_multiple_calls)}" + "#{format_args(args_for_one_call)}#{group_count(index, args_for_multiple_calls)}" end.join("\n ") end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index fb2a0b0ae..3f0725d96 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -28,7 +28,7 @@ def called_max_times? def verify_messages_received InsertOntoBacktrace.line(@backtrace_line) do unless @received - @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil) + @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, []) end end end @@ -468,9 +468,9 @@ def unadvise(args) def generate_error if similar_messages.empty? - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args) + @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, expected_args) else - @error_generator.raise_similar_message_args_error(self, *@similar_messages) + @error_generator.raise_similar_message_args_error(self, @similar_messages) end end @@ -483,7 +483,7 @@ def expectation_count_type def description_for(verb) @error_generator.describe_expectation( verb, @message, @expected_received_count, - @actual_received_count, *expected_args + @actual_received_count, expected_args ) end @@ -522,7 +522,7 @@ def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, @failed_fast = true # args are the args we actually received, @argument_list_matcher is the # list of args we were expecting - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args) + @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, args) end @order_group.handle_order_constraint self diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 2f5720fe0..528968b1b 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -105,7 +105,7 @@ def check_for_unexpected_arguments(expectation) !expectation.matches_name_but_not_args(method_name, *args) end - raise_unexpected_message_args_error(expectation, *messages_arg_list) + raise_unexpected_message_args_error(expectation, messages_arg_list) end # @private @@ -180,11 +180,11 @@ def message_received(message, *args, &block) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? if null_object? || !has_negative_expectation?(message) - raise_unexpected_message_args_error(expectation, args) + raise_unexpected_message_args_error(expectation, [args]) end elsif (stub = find_almost_matching_stub(message, *args)) stub.advise(*args) - raise_missing_default_stub_error(stub, args) + raise_missing_default_stub_error(stub, [args]) elsif Class === @object @object.superclass.__send__(message, *args, &block) else @@ -193,18 +193,18 @@ def message_received(message, *args, &block) end # @private - def raise_unexpected_message_error(method_name, *args) - @error_generator.raise_unexpected_message_error method_name, *args + def raise_unexpected_message_error(method_name, args) + @error_generator.raise_unexpected_message_error method_name, args end # @private - def raise_unexpected_message_args_error(expectation, *args) - @error_generator.raise_unexpected_message_args_error(expectation, *args) + def raise_unexpected_message_args_error(expectation, args) + @error_generator.raise_unexpected_message_args_error(expectation, args) end # @private - def raise_missing_default_stub_error(expectation, *args) - @error_generator.raise_missing_default_stub_error(expectation, *args) + def raise_missing_default_stub_error(expectation, args) + @error_generator.raise_missing_default_stub_error(expectation, args) end # @private diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 3b2056ded..5f0509f0c 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -98,7 +98,7 @@ def method_missing(message, *args, &block) # Required wrapping doubles in an Array on Ruby 1.9.2 raise NoMethodError if [:to_a, :to_ary].include? message - proxy.raise_unexpected_message_error(message, *args) + proxy.raise_unexpected_message_error(message, args) end def assign_stubs(stubs) From f40fce009765aa79785dc42341e12d3947355ef4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 11:56:49 -0700 Subject: [PATCH 0868/1179] Standardize on `args_for_multiple_calls`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s confusing to call it `args` when it’s a list of received args for multiple calls (an array-of-arrays). --- lib/rspec/mocks/error_generator.rb | 4 ++-- lib/rspec/mocks/proxy.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 772d21cc5..2c7afb29a 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -55,8 +55,8 @@ def raise_unexpected_message_args_error(expectation, args_for_multiple_calls) end # @private - def raise_missing_default_stub_error(expectation, args) - message = error_message(expectation, args) + def raise_missing_default_stub_error(expectation, args_for_multiple_calls) + message = error_message(expectation, args_for_multiple_calls) message << "\n Please stub a default value first if message might be received with other args as well. \n" __raise message diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 528968b1b..3f515e9d3 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -198,13 +198,13 @@ def raise_unexpected_message_error(method_name, args) end # @private - def raise_unexpected_message_args_error(expectation, args) - @error_generator.raise_unexpected_message_args_error(expectation, args) + def raise_unexpected_message_args_error(expectation, args_for_multiple_calls) + @error_generator.raise_unexpected_message_args_error(expectation, args_for_multiple_calls) end # @private - def raise_missing_default_stub_error(expectation, args) - @error_generator.raise_missing_default_stub_error(expectation, args) + def raise_missing_default_stub_error(expectation, args_for_multiple_calls) + @error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls) end # @private From 86e007f2e9afe8b14f2ab4c31cd9a8b6089cb70a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 11:58:01 -0700 Subject: [PATCH 0869/1179] Inline `arg_message`. --- lib/rspec/mocks/error_generator.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 2c7afb29a..18c5c1f11 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -46,7 +46,7 @@ def opts # @private def raise_unexpected_message_error(message, args) - __raise "#{intro} received unexpected message :#{message}#{arg_message(args)}" + __raise "#{intro} received unexpected message :#{message} with #{format_args(args)}" end # @private @@ -269,10 +269,6 @@ def __raise(message) Kernel.raise(RSpec::Mocks::MockExpectationError, message) end - def arg_message(args) - " with " + format_args(args) - end - def format_args(args) return "(no args)" if args.empty? "(#{arg_list(args)})" From 664fe81ef28b49093364600f5111c73afa97fab7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 12:03:03 -0700 Subject: [PATCH 0870/1179] Move methods into `private` section. They should not be public. --- lib/rspec/mocks/error_generator.rb | 57 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 18c5c1f11..d68f206fa 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -130,36 +130,6 @@ def raise_expired_test_double_error "one in each example you wish to use it for." end - # @private - def received_part_of_expectation_error(actual_received_count, args) - "received: #{count_message(actual_received_count)}" + - method_call_args_description(args) do - actual_received_count > 0 && args.length > 0 - end - end - - # @private - def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) - "expected: #{count_message(expected_received_count, expectation_count_type)}" + - method_call_args_description(argument_list_matcher.expected_args) do - argument_list_matcher.expected_args.length > 0 - end - end - - # @private - def method_call_args_description(args, &block) - case args.first - when ArgumentMatchers::AnyArgsMatcher then " with any arguments" - when ArgumentMatchers::NoArgsMatcher then " with no arguments" - else - if block.call - " with arguments: #{format_args(args)}" - else - "" - end - end - end - # @private def describe_expectation(verb, message, expected_received_count, _actual_received_count, args) "#{verb} #{message}#{format_args(args)} #{count_message(expected_received_count)}" @@ -207,6 +177,33 @@ def self.raise_double_negation_error(wrapped_expression) private + def received_part_of_expectation_error(actual_received_count, args) + "received: #{count_message(actual_received_count)}" + + method_call_args_description(args) do + actual_received_count > 0 && args.length > 0 + end + end + + def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) + "expected: #{count_message(expected_received_count, expectation_count_type)}" + + method_call_args_description(argument_list_matcher.expected_args) do + argument_list_matcher.expected_args.length > 0 + end + end + + def method_call_args_description(args, &block) + case args.first + when ArgumentMatchers::AnyArgsMatcher then " with any arguments" + when ArgumentMatchers::NoArgsMatcher then " with no arguments" + else + if block.call + " with arguments: #{format_args(args)}" + else + "" + end + end + end + def unexpected_arguments_message(expected_args_string, actual_args_string) "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}" end From dd8f8fdd8ae83023587fe459884a3ad01137923a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 12:03:31 -0700 Subject: [PATCH 0871/1179] `yield` performs better than `block.call` so use it. --- lib/rspec/mocks/error_generator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index d68f206fa..d82421281 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -191,12 +191,12 @@ def expected_part_of_expectation_error(expected_received_count, expectation_coun end end - def method_call_args_description(args, &block) + def method_call_args_description(args) case args.first when ArgumentMatchers::AnyArgsMatcher then " with any arguments" when ArgumentMatchers::NoArgsMatcher then " with no arguments" else - if block.call + if yield " with arguments: #{format_args(args)}" else "" From 27b9402e22779977cfb8841e41c6ca4d645c4577 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Apr 2015 22:44:48 -0700 Subject: [PATCH 0872/1179] Prefer `anything == object` over `object == anything`. Many users have objects that implement `==` wrongly in a way that can raise errors when given an object of an unexpected type. This avoids that situation by checking equality with `anything` instead. See rspec/rspec-expectations#732 for one case of this. --- Changelog.md | 3 ++ lib/rspec/mocks/argument_list_matcher.rb | 6 ++-- spec/rspec/mocks/argument_matchers_spec.rb | 34 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 30b78fe72..7152bc5a1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -26,6 +26,9 @@ Bug Fixes: arguments. Previously it confusingly listed the arguments as being mis-matched (even when the double was allowed to receive with any args) rather than listing the count. (John Ceh, #918) +* Fix `any_args`/`anything` support so that we avoid calling `obj == anything` + on user objects that may have improperly implemented `==` in a way that + raises errors. (Myron Marston, #924) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) diff --git a/lib/rspec/mocks/argument_list_matcher.rb b/lib/rspec/mocks/argument_list_matcher.rb index 453bf9f11..1ee647bb6 100644 --- a/lib/rspec/mocks/argument_list_matcher.rb +++ b/lib/rspec/mocks/argument_list_matcher.rb @@ -64,7 +64,7 @@ def args_match?(*args) def resolve_expected_args_based_on(actual_args) return [] if [ArgumentMatchers::NoArgsMatcher::INSTANCE] == expected_args - any_args_index = expected_args.index(ArgumentMatchers::AnyArgsMatcher::INSTANCE) + any_args_index = expected_args.index { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a } return expected_args unless any_args_index replace_any_args_with_splat_of_anything(any_args_index, actual_args.count) @@ -81,10 +81,10 @@ def replace_any_args_with_splat_of_anything(before_count, actual_args_count) end def ensure_expected_args_valid! - if expected_args.count(ArgumentMatchers::AnyArgsMatcher::INSTANCE) > 1 + if expected_args.count { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a } > 1 raise ArgumentError, "`any_args` can only be passed to " \ "`with` once but you have passed it multiple times." - elsif expected_args.count > 1 && expected_args.include?(ArgumentMatchers::NoArgsMatcher::INSTANCE) + elsif expected_args.count > 1 && expected_args.any? { |a| ArgumentMatchers::NoArgsMatcher::INSTANCE == a } raise ArgumentError, "`no_args` can only be passed as a " \ "singleton argument to `with` (i.e. `with(no_args)`), " \ "but you have passed additional arguments." diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index 2f113a7a9..f4f76fae7 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -321,6 +321,40 @@ module Mocks end end + context "handling objects with a wrong definition of `==` that raises errors for other types" do + Color = Struct.new(:r, :g, :b) do + def ==(other) + other.r == r && other.g == g && other.b == b + end + end + + before(:context) do + expect { Color.new(0, 0, 0) == Object.new }.to raise_error(NoMethodError) + end + + it 'matches against an equal instance of the same type' do + expect(a_double).to receive(:random_call).with(Color.new(0, 0, 0)) + a_double.random_call(Color.new(0, 0, 0)) + end + + it 'fails when matched against an unequal instance of the same class', :reset do + expect(a_double).to receive(:random_call).with(Color.new(0, 0, 0)) + expect { a_double.random_call(Color.new(0, 1, 0)) }.to fail + end + + it 'can match multiple instances of the type against multiple equal instances of the type' do + expect(a_double).to receive(:random_call).with( + Color.new(0, 0, 0), + Color.new(0, 1, 0) + ) + + a_double.random_call( + Color.new(0, 0, 0), + Color.new(0, 1, 0) + ) + end + end + context "handling non-matcher arguments" do it "matches string against regexp" do expect(a_double).to receive(:random_call).with(/bcd/) From 8a33557ea95ec1c5d08e6e93a3aa401d9d795d42 Mon Sep 17 00:00:00 2001 From: Samnang Chhun Date: Fri, 10 Apr 2015 21:40:49 +0700 Subject: [PATCH 0873/1179] Matching arguments with RSpec matchers --- .../matching_arguments.feature | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/features/setting_constraints/matching_arguments.feature b/features/setting_constraints/matching_arguments.feature index 638a2a368..0eb6f9b5f 100644 --- a/features/setting_constraints/matching_arguments.feature +++ b/features/setting_constraints/matching_arguments.feature @@ -46,6 +46,31 @@ Feature: Matching arguments | expected: (1, anything, /bar/) | | got: (1, nil, "other") | + Scenario: Using a RSpec matcher + Given a file named "rspec_matcher_spec.rb" with: + """ruby + RSpec.describe "Using a RSpec matcher" do + let(:dbl) { double } + before { expect(dbl).to receive(:foo).with(a_collection_containing_exactly(1, 2)) } + + it "passes when the args match" do + dbl.foo([2, 1]) + end + + it "fails when the args do not match" do + dbl.foo([1, 3]) + end + end + """ + When I run `rspec rspec_matcher_spec.rb` + Then it should fail with the following output: + | 2 examples, 1 failure | + | | + | Failure/Error: dbl.foo([1, 3]) | + | Double received :foo with unexpected arguments | + | expected: (a collection containing exactly 1 and 2) | + | got: ([1, 3]) | + Scenario: Using a custom matcher Given a file named "custom_matcher_spec.rb" with: """ruby From 34e91d95c21e27fa580b1cfa44645d935b679a89 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 13 Apr 2015 09:10:31 +1000 Subject: [PATCH 0874/1179] avoid using bare raise_error --- spec/rspec/mocks/double_spec.rb | 6 +++--- spec/rspec/mocks/multiple_return_value_spec.rb | 6 +++--- spec/rspec/mocks/stub_spec.rb | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 4dac642fa..0063a856e 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -776,7 +776,7 @@ def add_call expect(@double).to receive(:foo).with('bar') expect do @double.foo('baz') - end.to raise_error + end.to fail reset @double end end @@ -788,7 +788,7 @@ def add_call expect(@double).to receive(:foo).with(d1) expect do @double.foo(d2) - end.to raise_error + end.to fail reset @double end end @@ -800,7 +800,7 @@ def add_call expect(@double).to receive(:foo).with(d1) expect do @double.foo(d2) - end.to raise_error + end.to fail reset @double end end diff --git a/spec/rspec/mocks/multiple_return_value_spec.rb b/spec/rspec/mocks/multiple_return_value_spec.rb index 42a858901..7e763cda6 100644 --- a/spec/rspec/mocks/multiple_return_value_spec.rb +++ b/spec/rspec/mocks/multiple_return_value_spec.rb @@ -38,7 +38,7 @@ module Mocks it "fails when there are too few calls (if there is no stub)" do @double.do_something @double.do_something - expect { verify @double }.to raise_error + expect { verify @double }.to fail end it "fails when there are too many calls (if there is no stub)" do @@ -46,7 +46,7 @@ module Mocks @double.do_something @double.do_something @double.do_something - expect { verify @double }.to raise_error + expect { verify @double }.to fail end end @@ -116,7 +116,7 @@ module Mocks it "fails when called less than the specified number" do @double.do_something @double.do_something - expect { verify @double }.to raise_error + expect { verify @double }.to fail end it "fails fast when called greater than the specified number" do diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index d210a9569..ebde9681d 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -519,7 +519,7 @@ def object.existing_method; :original_value; end @stub.foo("baz") expect { @stub.foo("other") - }.to raise_error + }.to fail end it 'uses the correct stubbed response when responding to a mock expectation' do From 92bcc349d09b676a535a0cfd6297cb78658a44c6 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 13 Apr 2015 11:54:11 +1000 Subject: [PATCH 0875/1179] support `fail_with { }` and `fail {}` --- spec/support/matchers.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index ec967a445..5bde8b6f7 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -1,11 +1,11 @@ module RSpec module Matchers - def fail - raise_error(RSpec::Mocks::MockExpectationError) + def fail(&block) + raise_error(RSpec::Mocks::MockExpectationError, &block) end - def fail_with(*args) - raise_error(RSpec::Mocks::MockExpectationError, *args) + def fail_with(*args, &block) + raise_error(RSpec::Mocks::MockExpectationError, *args, &block) end def fail_including(*snippets) From beecd5c7c2e8b836476b1f8a27ceca64b4bdf67c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 13 Apr 2015 12:03:44 +1000 Subject: [PATCH 0876/1179] be consistent and use fail/fail_with everywhere --- spec/rspec/mocks/and_yield_spec.rb | 6 +-- spec/rspec/mocks/any_instance_spec.rb | 38 ++++++++--------- spec/rspec/mocks/double_spec.rb | 41 +++++++++---------- .../matchers/receive_message_chain_spec.rb | 6 +-- .../mocks/matchers/receive_messages_spec.rb | 4 +- spec/rspec/mocks/matchers/receive_spec.rb | 4 +- spec/rspec/mocks/mock_ordering_spec.rb | 8 ++-- .../rspec/mocks/multiple_return_value_spec.rb | 4 +- spec/rspec/mocks/null_object_double_spec.rb | 2 +- spec/rspec/mocks/once_counts_spec.rb | 4 +- spec/rspec/mocks/partial_double_spec.rb | 10 ++--- ...artial_double_using_mocks_directly_spec.rb | 4 +- spec/rspec/mocks/precise_counts_spec.rb | 4 +- spec/rspec/mocks/should_syntax_spec.rb | 12 +++--- .../stubbed_message_expectations_spec.rb | 2 +- .../syntax_agnostic_message_matchers_spec.rb | 2 +- spec/rspec/mocks/thrice_counts_spec.rb | 8 ++-- spec/rspec/mocks/twice_counts_spec.rb | 6 +-- spec/rspec/mocks_spec.rb | 2 +- spec/spec_helper.rb | 3 +- 20 files changed, 83 insertions(+), 87 deletions(-) diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index ab974767c..f902b3ee9 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -74,7 +74,7 @@ # foo is not called here end - expect { verify configured_eval_context }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify configured_eval_context }.to fail end end @@ -150,8 +150,8 @@ # foo is not called here end - expect { verify configured_eval_context }.to raise_error(RSpec::Mocks::MockExpectationError) - expect { verify yielded_arg }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify configured_eval_context }.to fail + expect { verify yielded_arg }.to fail end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 6303bc29b..6ef008b90 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -160,7 +160,7 @@ def private_method; :private_method_return_value; end it "fails the spec with an expectation error when the arguments do not match" do expect do klass.new.foo(:param_one, :param_three) - end.to(raise_error(RSpec::Mocks::MockExpectationError)) + end.to fail end end @@ -584,14 +584,14 @@ def inspect expect_any_instance_of(klass).to receive(:foo) klass.new verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) + end.to fail_with foo_expectation_error_message end it "fails if no instance is created" do expect do expect_any_instance_of(klass).to receive(:foo).and_return(1) verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) + end.to fail_with foo_expectation_error_message end it "fails if no instance is created and there are multiple expectations" do @@ -599,7 +599,7 @@ def inspect expect_any_instance_of(klass).to receive(:foo) expect_any_instance_of(klass).to receive(:bar) verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, 'Exactly one instance should have received the following message(s) but didn\'t: bar, foo') + end.to fail_with 'Exactly one instance should have received the following message(s) but didn\'t: bar, foo' end it "allows expectations on instances to take priority" do @@ -626,7 +626,7 @@ def inspect instance_one.foo instance_two.foo - end.to raise_error(RSpec::Mocks::MockExpectationError, /The message 'foo' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) + end.to fail_with(/The message 'foo' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end @@ -637,7 +637,7 @@ def inspect expect(klass).to receive(:woot) klass.new.foo verify_all - end.to(raise_error(RSpec::Mocks::MockExpectationError) do |error| + end.to(fail do |error| expect(error.message).not_to eq(existing_method_expectation_error_message) end) end @@ -662,14 +662,14 @@ def inspect expect_any_instance_of(klass).to receive(:existing_method) klass.new verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) + end.to fail_with existing_method_expectation_error_message end it "fails if no instance is created" do expect do expect_any_instance_of(klass).to receive(:existing_method) verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) + end.to fail_with existing_method_expectation_error_message end it "fails if no instance is created and there are multiple expectations" do @@ -677,7 +677,7 @@ def inspect expect_any_instance_of(klass).to receive(:existing_method) expect_any_instance_of(klass).to receive(:another_existing_method) verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, 'Exactly one instance should have received the following message(s) but didn\'t: another_existing_method, existing_method') + end.to fail_with 'Exactly one instance should have received the following message(s) but didn\'t: another_existing_method, existing_method' end context "after any one instance has received a message" do @@ -695,7 +695,7 @@ def inspect instance_one.existing_method instance_two.existing_method - end.to raise_error(RSpec::Mocks::MockExpectationError, /The message 'existing_method' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) + end.to fail_with(/The message 'existing_method' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end end @@ -736,7 +736,7 @@ def foo; end expect do expect(instances[0].foo(:param_one, :param_two)).to eq(:result_one) expect(instances[1].foo(:param_three, :param_four)).to eq(:result_two) - end.to raise_error(RSpec::Mocks::MockExpectationError) + end.to fail # ignore the fact that should_receive expectations were not met instances.each { |instance| reset instance } @@ -753,7 +753,7 @@ def foo; end instance = klass.new expect do instance.foo(:param_one, :param_three) - end.to raise_error(RSpec::Mocks::MockExpectationError) + end.to fail # ignore the fact that should_receive expectations were not met reset instance @@ -771,7 +771,7 @@ def foo; end expect do expect_any_instance_of(klass).to receive(:foo).once verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) + end.to fail_with foo_expectation_error_message end it "fails when an instance is declared but there are no invocations" do @@ -779,7 +779,7 @@ def foo; end expect_any_instance_of(klass).to receive(:foo).once klass.new verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, foo_expectation_error_message) + end.to fail_with foo_expectation_error_message end it "fails for more than one invocation" do @@ -830,7 +830,7 @@ def foo; end instance = klass.new 2.times { instance.foo } verify instance - end.to raise_error(RSpec::Mocks::MockExpectationError) + end.to fail end end @@ -847,7 +847,7 @@ def foo; end instance = klass.new 2.times { instance.foo } verify instance - end.to raise_error(RSpec::Mocks::MockExpectationError) + end.to fail end it "fails for n invocations where n > 3" do @@ -872,7 +872,7 @@ def foo; end instance = klass.new 2.times { instance.foo } verify instance - end.to raise_error(RSpec::Mocks::MockExpectationError) + end.to fail end it "passes for n invocations where n > 3" do @@ -929,7 +929,7 @@ def foo; end expect_any_instance_of(klass).to receive(:foo).never expect_any_instance_of(klass).to receive(:existing_method).and_return(5) verify_all - end.to raise_error(RSpec::Mocks::MockExpectationError, existing_method_expectation_error_message) + end.to fail_with existing_method_expectation_error_message end end end @@ -1195,7 +1195,7 @@ def dup(funky_option) expect_any_instance_of(klass).to receive(:existing_method) instance_one.existing_method instance_two.existing_method - end.to raise_error(RSpec::Mocks::MockExpectationError, /The message 'existing_method' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) + end.to fail_with(/The message 'existing_method' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 0063a856e..e386bffb4 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -41,7 +41,7 @@ module Mocks b = double('b') expect(a).to receive(:append).with(b) verify_all - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it 'allows string representation of methods in constructor' do @@ -78,7 +78,7 @@ module Mocks expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) expect { verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError) { |e| + }.to fail { |e| # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) } @@ -89,7 +89,7 @@ module Mocks expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) expect { verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError) { |e| + }.to fail { |e| # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) } @@ -217,7 +217,7 @@ module Mocks expect(@double).to receive(:something).with("a","b","c").and_return("booh") expect { @double.something("a","d","c") - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") + }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end describe "even when a similar expectation with different arguments exist" do @@ -227,7 +227,7 @@ module Mocks expect { @double.something("a","b","c") @double.something("z","x","g") - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"z\", \"x\", \"c\")\n got: (\"z\", \"x\", \"g\")") + }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (\"z\", \"x\", \"c\")\n got: (\"z\", \"x\", \"g\")" end end @@ -237,7 +237,7 @@ module Mocks expect { @double.something("a","d","c") verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") + }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end it "raises exception if args don't match when method called even when using null_object" do @@ -246,7 +246,7 @@ module Mocks expect { @double.something("a","d","c") verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")") + }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end describe 'with a method that has a default argument' do @@ -257,14 +257,14 @@ def @double.method_with_default_argument(arg={}); end expect { @double.method_with_default_argument(nil) verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError, a_string_starting_with("Double \"test double\" received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)")) + }.to fail_with a_string_starting_with("Double \"test double\" received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)") end end it "fails if unexpected method called" do expect { @double.something("a","b","c") - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received unexpected message :something with (\"a\", \"b\", \"c\")") + }.to fail_with "Double \"test double\" received unexpected message :something with (\"a\", \"b\", \"c\")" end it "uses block for expectation if provided" do @@ -336,9 +336,7 @@ def @double.method_with_default_argument(arg={}); end it "fails right away when method defined as never is received" do expect(@double).to receive(:not_expected).never expect { @double.not_expected }. - to raise_error(RSpec::Mocks::MockExpectationError, - %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| - ) + to fail_with %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| end it "raises RuntimeError by default" do @@ -389,7 +387,7 @@ def initialize(amount, units) expect(@double).to receive(:something).with(2).and_raise(RuntimeError) expect { @double.something 1 - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "throws when told to" do @@ -412,14 +410,14 @@ def initialize(amount, units) expect(@double).to receive(:something).with(no_args()) expect { @double.something 1 - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (no args)\n got: (1)") + }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (no args)\n got: (1)" end it "fails when args are expected but none are received" do expect(@double).to receive(:something).with(1) expect { @double.something - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :something with unexpected arguments\n expected: (1)\n got: (no args)") + }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (1)\n got: (no args)" end it "returns value from block by default" do @@ -523,7 +521,7 @@ def initialize(amount, units) expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') expect { @double.yield_back {|a|} - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" yielded |\"wha\", \"zup\"| to block with arity of 1") + }.to fail_with "Double \"test double\" yielded |\"wha\", \"zup\"| to block with arity of 1" end if kw_args_supported? @@ -531,8 +529,7 @@ def initialize(amount, units) expect(@double).to receive(:yield_back).and_yield(:x => 1, :y => 2) expect { eval("@double.yield_back { |x: 1| }") - }.to raise_error(RSpec::Mocks::MockExpectationError, - 'Double "test double" yielded |{:x=>1, :y=>2}| to block with optional keyword args (:x)') + }.to fail_with 'Double "test double" yielded |{:x=>1, :y=>2}| to block with optional keyword args (:x)' end end @@ -543,14 +540,14 @@ def initialize(amount, units) expect { c = [] @double.yield_back {|a,b| c << [a, b]} - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" yielded |\"down\"| to block with arity of 2") + }.to fail_with "Double \"test double\" yielded |\"down\"| to block with arity of 2" end it "fails when calling yielding method without block" do expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') expect { @double.yield_back - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" asked to yield |[\"wha\", \"zup\"]| but no block was passed") + }.to fail_with "Double \"test double\" asked to yield |[\"wha\", \"zup\"]| but no block was passed" end it "is able to double send" do @@ -577,7 +574,7 @@ def initialize(amount, units) verify @double expect { @double.foobar - }.to raise_error(RSpec::Mocks::MockExpectationError, %q|Double "test double" received unexpected message :foobar with (no args)|) + }.to fail_with %q|Double "test double" received unexpected message :foobar with (no args)| end it "restores objects to their original state on rspec_reset" do @@ -608,7 +605,7 @@ def initialize(amount, units) it "raises an error when a previously stubbed method has a negative expectation" do allow(@double).to receive(:msg).and_return(:stub_value) expect(@double).not_to receive(:msg) - expect { @double.msg(:arg) }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { @double.msg(:arg) }.to fail end it "temporarily replaces a method stub on a non-double" do diff --git a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb index ea2281899..a5ffa6c8e 100644 --- a/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +++ b/spec/rspec/mocks/matchers/receive_message_chain_spec.rb @@ -123,7 +123,7 @@ module RSpec::Mocks::Matchers expect(object).to receive_message_chain(:to_a, :farce, :length => 3) object.to_a verify_all - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "raises when expect is used and all but the last message in the chain are called" do @@ -131,7 +131,7 @@ module RSpec::Mocks::Matchers expect(object).to receive_message_chain(:foo, :bar, :baz) object.foo.bar verify_all - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "does not raise when expect is used and the entire chain is called" do @@ -163,7 +163,7 @@ module RSpec::Mocks::Matchers expect { expect_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) verify_all - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "affects previously stubbed instances when `expect_any_instance_of` is called" do diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 22d088067..4c6e729e8 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -81,7 +81,7 @@ module Mocks it "sets up multiple expectations" do expect(obj).to receive_messages(:a => 1, :b => 2) obj.a - expect { verify_all }.to raise_error RSpec::Mocks::MockExpectationError + expect { verify_all }.to fail end it 'fails with a sensible message' do @@ -112,7 +112,7 @@ module Mocks it "sets up multiple expectations" do expect_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) obj.a - expect { RSpec::Mocks.space.verify_all }.to raise_error RSpec::Mocks::MockExpectationError + expect { RSpec::Mocks.space.verify_all }.to fail RSpec::Mocks.space.reset_all end diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index e33822a69..bb66cefb1 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -152,7 +152,7 @@ module Mocks expect { verify_all - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "reports the line number of expectation of unreceived message", :pending => options.include?(:does_not_report_line_num) do @@ -449,7 +449,7 @@ def receiver.method_missing(*a); end # a poor man's stub... expect(dbl).to receive(:foo) end - expect { verify dbl }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify dbl }.to fail end it 'supports `expect(...).not_to receive`' do diff --git a/spec/rspec/mocks/mock_ordering_spec.rb b/spec/rspec/mocks/mock_ordering_spec.rb index 57821caef..0e4ef0629 100644 --- a/spec/rspec/mocks/mock_ordering_spec.rb +++ b/spec/rspec/mocks/mock_ordering_spec.rb @@ -41,7 +41,7 @@ module Mocks expect(@double).to receive(:two).ordered expect { @double.two - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :two out of order") + }.to fail_with "Double \"test double\" received :two out of order" end it "fails when messages are received out of order (3rd message 1st)" do @@ -51,7 +51,7 @@ module Mocks @double.one expect { @double.three - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :three out of order") + }.to fail_with "Double \"test double\" received :three out of order" end it "fails when messages are received out of order (3rd message 2nd)" do @@ -61,7 +61,7 @@ module Mocks @double.one expect { @double.three - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :three out of order") + }.to fail_with "Double \"test double\" received :three out of order" end it "fails when messages are out of order across objects" do @@ -73,7 +73,7 @@ module Mocks a.one expect { a.three - }.to raise_error(RSpec::Mocks::MockExpectationError, "Double \"test double\" received :three out of order") + }.to fail_with "Double \"test double\" received :three out of order" reset a reset b end diff --git a/spec/rspec/mocks/multiple_return_value_spec.rb b/spec/rspec/mocks/multiple_return_value_spec.rb index 7e763cda6..a83ae305c 100644 --- a/spec/rspec/mocks/multiple_return_value_spec.rb +++ b/spec/rspec/mocks/multiple_return_value_spec.rb @@ -80,7 +80,7 @@ module Mocks it "fails when called less than the specified number" do expect(@double.do_something).to equal(11) - expect { verify @double }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify @double }.to fail end context "when method is stubbed too" do @@ -95,7 +95,7 @@ module Mocks it "fails when called less than the specified number" do expect(@double.do_something).to equal(11) - expect { verify @double }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify @double }.to fail end end end diff --git a/spec/rspec/mocks/null_object_double_spec.rb b/spec/rspec/mocks/null_object_double_spec.rb index 09e2d68e4..6c321e5c0 100644 --- a/spec/rspec/mocks/null_object_double_spec.rb +++ b/spec/rspec/mocks/null_object_double_spec.rb @@ -63,7 +63,7 @@ module Mocks expect { expect(@double).to receive(:something) verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "ignores unexpected methods" do diff --git a/spec/rspec/mocks/once_counts_spec.rb b/spec/rspec/mocks/once_counts_spec.rb index 86ca70b35..d44d276d0 100644 --- a/spec/rspec/mocks/once_counts_spec.rb +++ b/spec/rspec/mocks/once_counts_spec.rb @@ -27,7 +27,7 @@ module Mocks expect(@double).to receive(:do_something).once.with("a", "b", "c") expect { @double.do_something("d", "e", "f") - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail reset @double end @@ -43,7 +43,7 @@ module Mocks expect(@double).to receive(:do_something).once expect { verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end context "when called with the wrong number of times with the specified args and also called with different args" do diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 1233ce6a8..9634c54ff 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -13,14 +13,14 @@ module Mocks expect(object).to receive(:foo) expect do verify object - end.to raise_error(RSpec::Mocks::MockExpectationError, /\(#\).foo/) + end.to fail_with(/\(#\).foo/) end it "names the class in the failure message when expectation is on class" do expect(Object).to receive(:foo) expect { verify Object - }.to raise_error(RSpec::Mocks::MockExpectationError, //) + }.to fail_with(//) end it "does not conflict with @options in the object" do @@ -78,7 +78,7 @@ module Mocks expect(object).to receive(:foobar).with(:test_param).and_return(1) expect { verify object - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "can accept the string form of a message for a positive message expectation" do @@ -119,7 +119,7 @@ def self.inspect expect { klazz.bar(2) - }.to raise_error(RSpec::Mocks::MockExpectationError, /MyClass/) + }.to fail_with(/MyClass/) end it "shares message expectations with clone" do @@ -134,7 +134,7 @@ def self.inspect expect(object).to receive(:foobar) duplicate = object.dup expect{ duplicate.foobar }.to raise_error(NoMethodError, /foobar/) - expect{ verify object }.to raise_error(RSpec::Mocks::MockExpectationError, /foobar/) + expect{ verify object }.to fail_with(/foobar/) end end diff --git a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb index 605a16e8c..3197bbb5c 100644 --- a/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb +++ b/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb @@ -29,14 +29,14 @@ def existing_method expect(obj).to receive(:msg) expect { verify obj - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "fails when message is received with incorrect args" do expect(obj).to receive(:msg).with(:correct_arg) expect { obj.msg(:incorrect_arg) - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail obj.msg(:correct_arg) end diff --git a/spec/rspec/mocks/precise_counts_spec.rb b/spec/rspec/mocks/precise_counts_spec.rb index 276b44028..a192ea88a 100644 --- a/spec/rspec/mocks/precise_counts_spec.rb +++ b/spec/rspec/mocks/precise_counts_spec.rb @@ -11,7 +11,7 @@ module Mocks @double.do_something expect { verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "fails fast when exactly n times method is called more than n times" do @@ -28,7 +28,7 @@ module Mocks expect(@double).to receive(:do_something).exactly(3).times expect { verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "passes if exactly n times method is called exactly n times" do diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index 61f91ddea..10243e161 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -64,7 +64,7 @@ def use_rspec_mocks expect(dbl.foo.bar.baz).to eq(17) expect { dbl.foo.baz.bar - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end include_examples "fails in a before(:all) block" do @@ -125,7 +125,7 @@ def obj.foo; :original; end obj = Object.new expect { obj.unstub(:foo) - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end end @@ -133,7 +133,7 @@ def obj.foo; :original; end it 'fails on verification if the message is not received' do dbl = double dbl.should_receive(:foo) - expect { verify_all }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify_all }.to fail end it 'does not fail on verification if the message is received' do @@ -194,7 +194,7 @@ def use_rspec_mocks it 'fails when the message is received' do with_unfulfilled_double do |dbl| dbl.should_not_receive(:foo) - expect { dbl.foo }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { dbl.foo }.to fail end end @@ -242,7 +242,7 @@ def use_rspec_mocks it 'can mock a method' do klass.any_instance.should_receive(:foo) klass.new - expect { verify_all }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify_all }.to fail end it 'can get method objects for the fluent interface', :if => RUBY_VERSION.to_f > 1.8 do @@ -443,7 +443,7 @@ def use_rspec_mocks it "raises a MockExpectationError if the method has not been stubbed" do expect { klass.any_instance.unstub(:existing_method) - }.to raise_error(RSpec::Mocks::MockExpectationError, 'The method `existing_method` was not stubbed or was already unstubbed') + }.to fail_with 'The method `existing_method` was not stubbed or was already unstubbed' end it 'does not get confused about string vs symbol usage for the message' do diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index b3021aff0..ca0fa1a1b 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -3,7 +3,7 @@ dbl = double(:msg => nil) dbl.msg expect(dbl).to receive(:msg) - expect { verify dbl }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify dbl }.to fail end it "outputs arguments of similar calls" do diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index 816f7e599..b91515f9a 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -55,7 +55,7 @@ module Mocks subject.respond_to?(:basic) }.to(true) - expect { verify subject }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { verify subject }.to fail end it "fails if never is specified and the message is called" do diff --git a/spec/rspec/mocks/thrice_counts_spec.rb b/spec/rspec/mocks/thrice_counts_spec.rb index 82821a1ad..6a8106fdc 100644 --- a/spec/rspec/mocks/thrice_counts_spec.rb +++ b/spec/rspec/mocks/thrice_counts_spec.rb @@ -38,14 +38,14 @@ module Mocks @double.do_something expect { verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "fails when called with wrong args on the first call" do expect(@double).to receive(:do_something).thrice.with("1", 1) expect { @double.do_something(1, "1") - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail reset @double end @@ -54,7 +54,7 @@ module Mocks @double.do_something("1", 1) expect { @double.do_something(1, "1") - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail reset @double end @@ -64,7 +64,7 @@ module Mocks @double.do_something("1", 1) expect { @double.do_something(1, "1") - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail reset @double end end diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index e8365076a..917f98ab2 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -40,14 +40,14 @@ module Mocks @double.do_something expect { verify @double - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail end it "fails when called with wrong args on the first call" do expect(@double).to receive(:do_something).twice.with("1", 1) expect { @double.do_something(1, "1") - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail reset @double end @@ -56,7 +56,7 @@ module Mocks @double.do_something("1", 1) expect { @double.do_something(1, "1") - }.to raise_error(RSpec::Mocks::MockExpectationError) + }.to fail reset @double end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 10837da6d..94d78438f 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -33,7 +33,7 @@ expect(foo).to receive(:bar) expect do RSpec::Mocks.verify - end.to raise_error(RSpec::Mocks::MockExpectationError) + end.to fail RSpec::Mocks.teardown # so the mocks aren't re-verified after this example end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c59beda9c..2edc9f29b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -55,8 +55,7 @@ def expect_fast_failure_from(double, *fail_with_args, &blk) module VerificationHelpers def prevents(msg = //, &block) - expect(&block).to \ - raise_error(RSpec::Mocks::MockExpectationError, msg) + expect(&block).to fail_with msg end end From ada66cfb2b43df43b2bdd9719b25278a82166ecd Mon Sep 17 00:00:00 2001 From: Siva Gollapalli Date: Thu, 9 Apr 2015 18:56:53 +0530 Subject: [PATCH 0877/1179] Registering doubles using ObjectInspector --- lib/rspec/mocks.rb | 1 + lib/rspec/mocks/test_double.rb | 4 ++++ spec/rspec/mocks/test_double_spec.rb | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 17b2831bc..243905595 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -2,6 +2,7 @@ RSpec::Support.require_rspec_support 'caller_filter' RSpec::Support.require_rspec_support 'warnings' RSpec::Support.require_rspec_support 'ruby_features' +RSpec::Support.require_rspec_support 'object_inspector' RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f } diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 5f0509f0c..6639e78b2 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -129,6 +129,10 @@ def initialize_copy(other) # A generic test double object. `double`, `instance_double` and friends # return an instance of this. class Double + RSpec::Support::ObjectInspector.register TestDouble do |dbl| + "Double (#{dbl.instance_variable_get('@name')})" + end + include TestDouble end end diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index a260d35dc..b339ec114 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -48,6 +48,11 @@ module Mocks end end end + + it "register itself with ObjectInspector" do + foobar = double('FooBar') + expect(RSpec::Support::ObjectInspector.inspect(foobar)).to eq("Double (FooBar)") + end end end end From 8fda7ef5bc28bcec6208b99af03daec029cef154 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 16 Apr 2015 23:15:42 -0700 Subject: [PATCH 0878/1179] Improve double formatting in error messages. - Include `Double` vs `InstanceDouble` vs `ClassDouble` vs `ObjectDouble`. - Handle doubles that have prevented or stubbed `instance_variable_get`. For rspec/rspec-expectations#688. --- lib/rspec/mocks/error_generator.rb | 15 ++-- lib/rspec/mocks/test_double.rb | 41 ++++++++- lib/rspec/mocks/verifying_double.rb | 12 --- spec/rspec/mocks/double_spec.rb | 2 +- spec/rspec/mocks/formatting_spec.rb | 90 +++++++++++++++++++ spec/rspec/mocks/test_double_spec.rb | 5 -- .../class_double_with_class_loaded_spec.rb | 2 +- ...class_double_with_class_not_loaded_spec.rb | 2 +- .../instance_double_with_class_loaded_spec.rb | 4 +- ...tance_double_with_class_not_loaded_spec.rb | 4 +- .../mocks/verifying_doubles/naming_spec.rb | 20 ++--- 11 files changed, 150 insertions(+), 47 deletions(-) create mode 100644 spec/rspec/mocks/formatting_spec.rb diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index d82421281..f1336eaf0 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -248,16 +248,11 @@ def differ end def intro - if @name - "Double #{@name.inspect}" - elsif TestDouble === @target - "Double" - elsif Class === @target - "<#{@target.inspect} (class)>" - elsif @target - @target - else - "nil" + case @target + when TestDouble then TestDoubleFormatter.format(@target, :unwrapped) + when Class then "<#{@target.inspect} (class)>" + when NilClass then "nil" + else @target end end diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 6639e78b2..510132e1c 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -129,11 +129,46 @@ def initialize_copy(other) # A generic test double object. `double`, `instance_double` and friends # return an instance of this. class Double - RSpec::Support::ObjectInspector.register TestDouble do |dbl| - "Double (#{dbl.instance_variable_get('@name')})" + include TestDouble + end + + # @private + module TestDoubleFormatter + def self.format(dbl, unwrap=false) + format = "#{type_desc(dbl)}#{verified_module_desc(dbl)} #{name_desc(dbl)}" + return format if unwrap + "#<#{format}>" end - include TestDouble + class << self + private + + def type_desc(dbl) + case dbl + when InstanceVerifyingDouble then "InstanceDouble" + when ClassVerifyingDouble then "ClassDouble" + when ObjectVerifyingDouble then "ObjectDouble" + else "Double" + end + end + + # @private + IVAR_GET = Object.instance_method(:instance_variable_get) + + def verified_module_desc(dbl) + return nil unless VerifyingDouble === dbl + "(#{IVAR_GET.bind(dbl).call(:@doubled_module).description})" + end + + def name_desc(dbl) + return "(anonymous)" unless (name = IVAR_GET.bind(dbl).call(:@name)) + name.inspect + end + end + end + + RSpec::Support::ObjectInspector.register TestDouble do |dbl| + TestDoubleFormatter.format(dbl) end end end diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index 7857a77a8..a0c750daf 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -62,8 +62,6 @@ def initialize(doubled_module, *args) possible_name = args.first name = if String === possible_name || Symbol === possible_name args.shift - else - @description end super(name, *args) @@ -80,11 +78,6 @@ class InstanceVerifyingDouble include TestDouble include VerifyingDouble - def initialize(doubled_module, *args) - @description = "#{doubled_module.description} (instance)" - super - end - def __build_mock_proxy(order_group) VerifyingProxy.new(self, order_group, @name, @doubled_module, @@ -108,11 +101,6 @@ def as_stubbed_const(options={}) private - def initialize(doubled_module, *args) - @description = doubled_module.description - super - end - def __build_mock_proxy(order_group) VerifyingProxy.new(self, order_group, @name, @doubled_module, diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index e386bffb4..7aa9d9283 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -738,7 +738,7 @@ def add_call describe "double created with no name" do it "does not use a name in a failure message" do double = double() - expect {double.foo}.to raise_error(/Double received/) + expect {double.foo}.to raise_error.with_message(a_string_including("Double (anonymous) received")) end it "does respond to initially stubbed methods" do diff --git a/spec/rspec/mocks/formatting_spec.rb b/spec/rspec/mocks/formatting_spec.rb new file mode 100644 index 000000000..4ced64b60 --- /dev/null +++ b/spec/rspec/mocks/formatting_spec.rb @@ -0,0 +1,90 @@ +require 'rspec/matchers/fail_matchers' +require 'support/doubled_classes' + +RSpec.describe "Test doubles format well in failure messages" do + RSpec::Matchers.define :format_in_failures_as do |expected| + match do |dbl| + values_match?(expected, actual_formatting(dbl)) + end + + def actual_formatting(double) + expect(1).to eq(double) + rescue RSpec::Expectations::ExpectationNotMetError => e + e.message[/expected: (.*)$/, 1] + else + raise "Did not fail as expected" + end + end + + describe "`double`" do + context "with a name" do + specify '#' do + expect(double "Book").to format_in_failures_as('#') + end + + it 'formats the name as a symbol if that was how it was provided' do + expect(double :book).to format_in_failures_as('#') + end + end + + context "without a name" do + specify '#' do + expect(double).to format_in_failures_as('#') + end + end + + it 'avoids sending `instance_variable_get` to the double as it may be stubbed' do + dbl = double("Book") + expect(dbl).not_to receive(:instance_variable_get) + expect(dbl).to format_in_failures_as('#') + end + end + + describe "`instance_double(SomeClass)`" do + context "with a name" do + specify '#' do + expect(instance_double(LoadedClass, "Book")).to format_in_failures_as('#') + end + end + + context "without a name" do + specify '#' do + expect(instance_double(LoadedClass)).to format_in_failures_as('#') + end + end + + it 'avoids sending `instance_variable_get` to the double as it may be stubbed' do + dbl = instance_double(LoadedClass, "Book") + expect(dbl).not_to receive(:instance_variable_get) + expect(dbl).to format_in_failures_as('#') + end + end + + describe "`class_double(SomeClass)`" do + context "with a name" do + specify '#' do + expect(class_double(LoadedClass, "Book")).to format_in_failures_as('#') + end + end + + context "without a name" do + specify '#' do + expect(class_double(LoadedClass)).to format_in_failures_as('#') + end + end + end + + describe "`object_double(:foo)`" do + context "with a name" do + specify '#' do + expect(object_double(:foo, "Name")).to format_in_failures_as('#') + end + end + + context "without a name" do + specify '#' do + expect(object_double(:foo)).to format_in_failures_as('#') + end + end + end +end diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index b339ec114..a260d35dc 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -48,11 +48,6 @@ module Mocks end end end - - it "register itself with ObjectInspector" do - foobar = double('FooBar') - expect(RSpec::Support::ObjectInspector.inspect(foobar)).to eq("Double (FooBar)") - end end end end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index c76c76f31..e03d85c55 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -44,7 +44,7 @@ module Mocks o = class_double("LoadedClass") expect { o.defined_private_class_method - }.to raise_error(NoMethodError, /Double "LoadedClass"/) + }.to raise_error(NoMethodError, a_string_including("ClassDouble(LoadedClass)")) end it 'checks that stubbed methods are invoked with the correct arity' do diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb index ac499681a..a41ee406e 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb @@ -13,7 +13,7 @@ module Mocks o = class_double("NonLoadedClass") expect { o.foo - }.to fail_including('Double "NonLoadedClass"') + }.to fail_including('ClassDouble(NonLoadedClass)') end it 'allows any method to be stubbed' do diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index fcc6a6a68..8ff35853e 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -51,7 +51,7 @@ module Mocks expect { o.defined_private_method }.to raise_error(NoMethodError, - /Double "LoadedClass \(instance\)"/) + a_string_including("InstanceDouble(LoadedClass)")) end it 'does not allow dynamic methods to be expected' do @@ -195,7 +195,7 @@ module Mocks it "includes the double's name in a private method error" do expect { obj.rand - }.to raise_error(NoMethodError, %r{private.*Double "LoadedClass \(instance\)"}) + }.to raise_error(NoMethodError, a_string_including("private", "InstanceDouble(LoadedClass)")) end it 'reports what public messages it responds to accurately' do diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb index f3ef3c3c5..b6a6aa25c 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb @@ -9,11 +9,11 @@ module Mocks RSpec::Mocks.configuration.verify_doubled_constant_names = false end - it 'includes the double name in errors for unexpected messages' do + it 'includes the doubled module in errors for unexpected messages' do o = instance_double("NonLoadedClass") expect { o.foo - }.to fail_including('Double "NonLoadedClass (instance)"') + }.to fail_including('InstanceDouble(NonLoadedClass)') end it 'allows any instance method to be stubbed' do diff --git a/spec/rspec/mocks/verifying_doubles/naming_spec.rb b/spec/rspec/mocks/verifying_doubles/naming_spec.rb index 806c787dc..392a91f68 100644 --- a/spec/rspec/mocks/verifying_doubles/naming_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/naming_spec.rb @@ -26,55 +26,55 @@ def error_message_for(verifying_double) end RSpec.describe 'Verified double naming' do - shared_examples "a named verifying double" do |default_name| + shared_examples "a named verifying double" do |type_desc| context "when a name is given as a string" do subject { create_double("LoadedClass", "foo") } - it { is_expected.to fail_expectations_as('Double "foo"') } + it { is_expected.to fail_expectations_as(%Q{#{type_desc}(LoadedClass) "foo"}) } end context "when a name is given as a symbol" do subject { create_double("LoadedClass", :foo) } - it { is_expected.to fail_expectations_as('Double :foo') } + it { is_expected.to fail_expectations_as(%Q{#{type_desc}(LoadedClass) :foo}) } end context "when no name is given" do subject { create_double("LoadedClass") } - it { is_expected.to fail_expectations_as("Double \"#{default_name}\"") } + it { is_expected.to fail_expectations_as(%Q{#{type_desc}(LoadedClass) (anonymous)}) } end end describe "instance_double" do - it_behaves_like "a named verifying double", "LoadedClass (instance)" do + it_behaves_like "a named verifying double", "InstanceDouble" do alias :create_double :instance_double end end describe "instance_spy" do - it_behaves_like "a named verifying double", "LoadedClass (instance)" do + it_behaves_like "a named verifying double", "InstanceDouble" do alias :create_double :instance_spy end end describe "class_double" do - it_behaves_like "a named verifying double", "LoadedClass" do + it_behaves_like "a named verifying double", "ClassDouble" do alias :create_double :class_double end end describe "class_spy" do - it_behaves_like "a named verifying double", "LoadedClass" do + it_behaves_like "a named verifying double", "ClassDouble" do alias :create_double :class_spy end end describe "object_double" do - it_behaves_like "a named verifying double", "LoadedClass" do + it_behaves_like "a named verifying double", "ObjectDouble" do alias :create_double :object_double end end describe "object_spy" do - it_behaves_like "a named verifying double", "LoadedClass" do + it_behaves_like "a named verifying double", "ObjectDouble" do alias :create_double :object_spy end end From 3e6f263503ff14665e5a3fae124e691ebec4a691 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 17 Apr 2015 00:09:28 -0700 Subject: [PATCH 0879/1179] Rename unused `@name` variable. --- lib/rspec/mocks/error_generator.rb | 3 +-- lib/rspec/mocks/proxy.rb | 5 ++--- lib/rspec/mocks/test_double.rb | 6 +++--- lib/rspec/mocks/verifying_double.rb | 4 ++-- lib/rspec/mocks/verifying_proxy.rb | 4 ++-- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index f1336eaf0..081dcd698 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -34,9 +34,8 @@ module Mocks class ErrorGenerator attr_writer :opts - def initialize(target, name) + def initialize(target) @target = target - @name = name end # @private diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 3f515e9d3..61493e412 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -14,11 +14,10 @@ def ensure_implemented(*_args) end # @private - def initialize(object, order_group, name=nil, options={}) + def initialize(object, order_group, options={}) @object = object @order_group = order_group - @name = name - @error_generator = ErrorGenerator.new(object, name) + @error_generator = ErrorGenerator.new(object) @messages_received = [] @options = options @null_object = false diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 510132e1c..c701c6a6c 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -91,7 +91,7 @@ def method_missing(message, *args, &block) # https://github.com/jruby/jruby/issues/1398 visibility = proxy.visibility_for(message) if visibility == :private || visibility == :protected - ErrorGenerator.new(self, @name).raise_non_public_error( + ErrorGenerator.new(self).raise_non_public_error( message, visibility ) end @@ -112,12 +112,12 @@ def __mock_proxy end def __build_mock_proxy(order_group) - TestDoubleProxy.new(self, order_group, @name) + TestDoubleProxy.new(self, order_group) end def __raise_expired_error return false unless @__expired - ErrorGenerator.new(self, @name).raise_expired_test_double_error + ErrorGenerator.new(self).raise_expired_test_double_error end def initialize_copy(other) diff --git a/lib/rspec/mocks/verifying_double.rb b/lib/rspec/mocks/verifying_double.rb index a0c750daf..39723b9fc 100644 --- a/lib/rspec/mocks/verifying_double.rb +++ b/lib/rspec/mocks/verifying_double.rb @@ -79,7 +79,7 @@ class InstanceVerifyingDouble include VerifyingDouble def __build_mock_proxy(order_group) - VerifyingProxy.new(self, order_group, @name, + VerifyingProxy.new(self, order_group, @doubled_module, InstanceMethodReference ) @@ -102,7 +102,7 @@ def as_stubbed_const(options={}) private def __build_mock_proxy(order_group) - VerifyingProxy.new(self, order_group, @name, + VerifyingProxy.new(self, order_group, @doubled_module, ObjectMethodReference ) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 2e1a11377..92845cb16 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -56,8 +56,8 @@ def ensure_publicly_implemented(method_name, _object) class VerifyingProxy < TestDoubleProxy include VerifyingProxyMethods - def initialize(object, order_group, name, doubled_module, method_reference_class) - super(object, order_group, name) + def initialize(object, order_group, doubled_module, method_reference_class) + super(object, order_group) @object = object @doubled_module = doubled_module @method_reference_class = method_reference_class From c8abe4e13f202d75e27ad621bc49b31542540067 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 17 Apr 2015 00:18:28 -0700 Subject: [PATCH 0880/1179] Only unwrap the formatted double for the case that uses parens. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s nice to have the `#<...>` bit otherwise. --- lib/rspec/mocks/error_generator.rb | 13 +++++--- spec/rspec/mocks/diffing_spec.rb | 14 ++++---- spec/rspec/mocks/double_spec.rb | 32 +++++++++---------- spec/rspec/mocks/mock_ordering_spec.rb | 8 ++--- spec/rspec/mocks/partial_double_spec.rb | 2 +- .../stubbed_message_expectations_spec.rb | 2 +- 6 files changed, 37 insertions(+), 34 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 081dcd698..d2f113235 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -80,7 +80,7 @@ def default_error_message(expectation, expected_args, actual_args) def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, args) expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) received_part = received_part_of_expectation_error(actual_received_count, args) - __raise "(#{intro}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}" + __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}" end # rubocop:enable Style/ParameterLists @@ -246,11 +246,14 @@ def differ RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?) end - def intro + def intro(unwrapped=false) case @target - when TestDouble then TestDoubleFormatter.format(@target, :unwrapped) - when Class then "<#{@target.inspect} (class)>" - when NilClass then "nil" + when TestDouble then TestDoubleFormatter.format(@target, unwrapped) + when Class then + formatted = "#{@target.inspect} (class)" + return formatted if unwrapped + "#<#{formatted}>" + when NilClass then "nil" else @target end end diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index 4fb41c2b4..0bc5c8163 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -21,7 +21,7 @@ expect(d).to receive(:foo).with("some string\nline2") expect { d.foo("this other string") - }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (\"some string\\nline2\")\n got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\n-line2\n+this other string\n") + }.to fail_with("# received :foo with unexpected arguments\n expected: (\"some string\\nline2\")\n got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\n-line2\n+this other string\n") end end @@ -30,7 +30,7 @@ expect(d).to receive(:foo).with("some string\nline2", "some other string") expect { d.foo("this other string") - }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (\"some string\\nline2\", \"some other string\")\n got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\\nline2\n-some other string\n+this other string\n") + }.to fail_with("# received :foo with unexpected arguments\n expected: (\"some string\\nline2\", \"some other string\")\n got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\\nline2\n-some other string\n+this other string\n") end end @@ -52,7 +52,7 @@ expect(d).to receive(:foo).with(expected_hash) expect { d.foo(:bad => :hash) - }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (#{expected_hash.inspect})\n got: (#{actual_hash.inspect})\nDiff:\n@@ -1,2 +1,2 @@\n-[#{expected_hash.inspect}]\n+[#{actual_hash.inspect}]\n") + }.to fail_with("# received :foo with unexpected arguments\n expected: (#{expected_hash.inspect})\n got: (#{actual_hash.inspect})\nDiff:\n@@ -1,2 +1,2 @@\n-[#{expected_hash.inspect}]\n+[#{actual_hash.inspect}]\n") end end @@ -70,7 +70,7 @@ expect(d).to receive(:foo).with([:a, :b, :c]) expect { d.foo([]) - }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: ([:a, :b, :c])\n got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[[:a, :b, :c]]\n+[[]]\n") + }.to fail_with("# received :foo with unexpected arguments\n expected: ([:a, :b, :c])\n got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[[:a, :b, :c]]\n+[[]]\n") end end @@ -84,7 +84,7 @@ expect(d).to receive(:foo).with(collab) expect { d.foo([]) - }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (#{collab_inspect})\n got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_inspect}]\n+[[]]\n") + }.to fail_with("# received :foo with unexpected arguments\n expected: (#{collab_inspect})\n got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_inspect}]\n+[[]]\n") end end end @@ -101,7 +101,7 @@ expect(d).to receive(:foo).with(collab) expect { d.foo([:a, :b]) - }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (#{collab_description})\n got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[\"#{collab_description}\"]\n+[[:a, :b]]\n") + }.to fail_with("# received :foo with unexpected arguments\n expected: (#{collab_description})\n got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[\"#{collab_description}\"]\n+[[:a, :b]]\n") end end end @@ -123,7 +123,7 @@ def self.name expect(d).to receive(:foo).with(collab) expect { d.foo([:a, :b]) - }.to fail_with("Double \"double\" received :foo with unexpected arguments\n expected: (#{collab_inspect})\n got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_pp}]\n+[[:a, :b]]\n") + }.to fail_with("# received :foo with unexpected arguments\n expected: (#{collab_inspect})\n got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_pp}]\n+[[:a, :b]]\n") end end end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 7aa9d9283..dde929ae0 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -14,7 +14,7 @@ module Mocks it "uses 'Double' in failure messages" do dbl = double('name') - expect { dbl.foo }.to raise_error(/Double "name" received/) + expect { dbl.foo }.to raise_error(/# received/) end it "hides internals in its inspect representation" do @@ -217,7 +217,7 @@ module Mocks expect(@double).to receive(:something).with("a","b","c").and_return("booh") expect { @double.something("a","d","c") - }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" + }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end describe "even when a similar expectation with different arguments exist" do @@ -227,7 +227,7 @@ module Mocks expect { @double.something("a","b","c") @double.something("z","x","g") - }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (\"z\", \"x\", \"c\")\n got: (\"z\", \"x\", \"g\")" + }.to fail_with "# received :something with unexpected arguments\n expected: (\"z\", \"x\", \"c\")\n got: (\"z\", \"x\", \"g\")" end end @@ -237,7 +237,7 @@ module Mocks expect { @double.something("a","d","c") verify @double - }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" + }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end it "raises exception if args don't match when method called even when using null_object" do @@ -246,7 +246,7 @@ module Mocks expect { @double.something("a","d","c") verify @double - }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" + }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end describe 'with a method that has a default argument' do @@ -257,14 +257,14 @@ def @double.method_with_default_argument(arg={}); end expect { @double.method_with_default_argument(nil) verify @double - }.to fail_with a_string_starting_with("Double \"test double\" received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)") + }.to fail_with a_string_starting_with("# received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)") end end it "fails if unexpected method called" do expect { @double.something("a","b","c") - }.to fail_with "Double \"test double\" received unexpected message :something with (\"a\", \"b\", \"c\")" + }.to fail_with "# received unexpected message :something with (\"a\", \"b\", \"c\")" end it "uses block for expectation if provided" do @@ -410,14 +410,14 @@ def initialize(amount, units) expect(@double).to receive(:something).with(no_args()) expect { @double.something 1 - }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (no args)\n got: (1)" + }.to fail_with "# received :something with unexpected arguments\n expected: (no args)\n got: (1)" end it "fails when args are expected but none are received" do expect(@double).to receive(:something).with(1) expect { @double.something - }.to fail_with "Double \"test double\" received :something with unexpected arguments\n expected: (1)\n got: (no args)" + }.to fail_with "# received :something with unexpected arguments\n expected: (1)\n got: (no args)" end it "returns value from block by default" do @@ -521,7 +521,7 @@ def initialize(amount, units) expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') expect { @double.yield_back {|a|} - }.to fail_with "Double \"test double\" yielded |\"wha\", \"zup\"| to block with arity of 1" + }.to fail_with "# yielded |\"wha\", \"zup\"| to block with arity of 1" end if kw_args_supported? @@ -529,7 +529,7 @@ def initialize(amount, units) expect(@double).to receive(:yield_back).and_yield(:x => 1, :y => 2) expect { eval("@double.yield_back { |x: 1| }") - }.to fail_with 'Double "test double" yielded |{:x=>1, :y=>2}| to block with optional keyword args (:x)' + }.to fail_with '# yielded |{:x=>1, :y=>2}| to block with optional keyword args (:x)' end end @@ -540,14 +540,14 @@ def initialize(amount, units) expect { c = [] @double.yield_back {|a,b| c << [a, b]} - }.to fail_with "Double \"test double\" yielded |\"down\"| to block with arity of 2" + }.to fail_with "# yielded |\"down\"| to block with arity of 2" end it "fails when calling yielding method without block" do expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') expect { @double.yield_back - }.to fail_with "Double \"test double\" asked to yield |[\"wha\", \"zup\"]| but no block was passed" + }.to fail_with "# asked to yield |[\"wha\", \"zup\"]| but no block was passed" end it "is able to double send" do @@ -574,7 +574,7 @@ def initialize(amount, units) verify @double expect { @double.foobar - }.to fail_with %q|Double "test double" received unexpected message :foobar with (no args)| + }.to fail_with %q|# received unexpected message :foobar with (no args)| end it "restores objects to their original state on rspec_reset" do @@ -731,14 +731,14 @@ def add_call double = double("Foo") expect { double.to_str }.to raise_error( RSpec::Mocks::MockExpectationError, - 'Double "Foo" received unexpected message :to_str with (no args)') + '# received unexpected message :to_str with (no args)') end end describe "double created with no name" do it "does not use a name in a failure message" do double = double() - expect {double.foo}.to raise_error.with_message(a_string_including("Double (anonymous) received")) + expect {double.foo}.to raise_error.with_message(a_string_including("# received")) end it "does respond to initially stubbed methods" do diff --git a/spec/rspec/mocks/mock_ordering_spec.rb b/spec/rspec/mocks/mock_ordering_spec.rb index 0e4ef0629..d4eebb799 100644 --- a/spec/rspec/mocks/mock_ordering_spec.rb +++ b/spec/rspec/mocks/mock_ordering_spec.rb @@ -41,7 +41,7 @@ module Mocks expect(@double).to receive(:two).ordered expect { @double.two - }.to fail_with "Double \"test double\" received :two out of order" + }.to fail_with "# received :two out of order" end it "fails when messages are received out of order (3rd message 1st)" do @@ -51,7 +51,7 @@ module Mocks @double.one expect { @double.three - }.to fail_with "Double \"test double\" received :three out of order" + }.to fail_with "# received :three out of order" end it "fails when messages are received out of order (3rd message 2nd)" do @@ -61,7 +61,7 @@ module Mocks @double.one expect { @double.three - }.to fail_with "Double \"test double\" received :three out of order" + }.to fail_with "# received :three out of order" end it "fails when messages are out of order across objects" do @@ -73,7 +73,7 @@ module Mocks a.one expect { a.three - }.to fail_with "Double \"test double\" received :three out of order" + }.to fail_with "# received :three out of order" reset a reset b end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 9634c54ff..e7f54effc 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -20,7 +20,7 @@ module Mocks expect(Object).to receive(:foo) expect { verify Object - }.to fail_with(//) + }.to fail_with(/Object \(class\)/) end it "does not conflict with @options in the object" do diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index ca0fa1a1b..0d0b89236 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -16,7 +16,7 @@ }.to raise_error( RSpec::Mocks::MockExpectationError, a_string_including( - %Q|Double "double" received :foo with unexpected arguments|, + %Q|# received :foo with unexpected arguments|, "expected: (\"first\")", "got:","(\"second\")", "(\"third\")")) From 4a894eacaf42e50b4ecd5d077964022d280a3aeb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 17 Apr 2015 00:48:02 -0700 Subject: [PATCH 0881/1179] Move double formatting logic into `inspect`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Makes doubles format well in data structures and diffs in failures. - The `inspect` we had before really wasn’t very good; this is much better, anyway. --- lib/rspec/mocks/test_double.rb | 6 +----- spec/rspec/mocks/double_spec.rb | 2 +- spec/rspec/mocks/formatting_spec.rb | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index c701c6a6c..3321ce1dc 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -39,7 +39,7 @@ def ==(other) # @private def inspect - "#<#{self.class}:#{'0x%x' % object_id} @name=#{@name.inspect}>" + TestDoubleFormatter.format(self) end # @private @@ -166,9 +166,5 @@ def name_desc(dbl) end end end - - RSpec::Support::ObjectInspector.register TestDouble do |dbl| - TestDoubleFormatter.format(dbl) - end end end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index dde929ae0..422f4f859 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -19,7 +19,7 @@ module Mocks it "hides internals in its inspect representation" do m = double('cup') - expect(m.inspect).to match(/#/) + expect(m.inspect).to eq('#') end it 'does not blow up when resetting standard object methods' do diff --git a/spec/rspec/mocks/formatting_spec.rb b/spec/rspec/mocks/formatting_spec.rb index 4ced64b60..c1ee58d3a 100644 --- a/spec/rspec/mocks/formatting_spec.rb +++ b/spec/rspec/mocks/formatting_spec.rb @@ -2,6 +2,8 @@ require 'support/doubled_classes' RSpec.describe "Test doubles format well in failure messages" do + include RSpec::Matchers::FailMatchers + RSpec::Matchers.define :format_in_failures_as do |expected| match do |dbl| values_match?(expected, actual_formatting(dbl)) @@ -87,4 +89,21 @@ def actual_formatting(double) end end end + + it 'formats the doubles when they appear in data structures and diffs' do + allow(RSpec::Expectations.configuration).to receive(:color?).and_return(false) + + foo = double("Foo") + bar = double("Bar") + + expect { + expect([foo]).to include(bar) + }.to fail_with(<<-EOS.gsub(/^\s+\|/, '')) + |expected [#] to include # + |Diff: + |@@ -1,2 +1,2 @@ + |-[#] + |+[#] + EOS + end end From 3d9fb62c267fe2fba77f6004d2d592b8bad7f8dd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 17 Apr 2015 00:51:39 -0700 Subject: [PATCH 0882/1179] Add changelog entry. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 7152bc5a1..43d77e85f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,8 @@ Enhancements: level methods. (Tim Wade, #896, #908) * Improve mock expectation failure messages so that it combines both number of times and the received arguments in the output. (John Ceh, #918) +* Improve how test doubles are represented in failure messages. + (@sivagollapalli, Myron Marston, #932) Bug Fixes: From 3f254fa61c01508764323a45fe82bb417c68a16e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 17 Apr 2015 07:50:33 -0700 Subject: [PATCH 0883/1179] Update cukes to match new test double formatting. --- features/basics/scope.feature | 10 +++--- features/basics/spies.feature | 24 ++++++------- features/basics/test_doubles.feature | 2 +- .../configuring_responses/yielding.feature | 4 +-- features/outside_rspec/minitest.feature | 26 +++++++------- .../matching_arguments.feature | 36 +++++++++---------- .../setting_constraints/message_order.feature | 4 +-- 7 files changed, 53 insertions(+), 53 deletions(-) diff --git a/features/basics/scope.feature b/features/basics/scope.feature index 1bc8a430c..26624134c 100644 --- a/features/basics/scope.feature +++ b/features/basics/scope.feature @@ -92,8 +92,8 @@ Feature: Scope """ When I run `rspec leak_test_double_spec.rb` Then it should fail with the following output: - | 2 examples, 1 failure | - | | - | 1) Account keeps track of the balance | - | Failure/Error: self.class.logger.log("Credited $#{amount}") | - | Double "Logger" was originally created in one example but has leaked into another example and can no longer be used. | + | 2 examples, 1 failure | + | | + | 1) Account keeps track of the balance | + | Failure/Error: self.class.logger.log("Credited $#{amount}") | + | # was originally created in one example but has leaked into another example and can no longer be used. | diff --git a/features/basics/spies.feature b/features/basics/spies.feature index c2e468cea..a6fe39f71 100644 --- a/features/basics/spies.feature +++ b/features/basics/spies.feature @@ -79,7 +79,7 @@ Feature: Spies """ 2) failure when the message has not been received for a partial double Failure/Error: expect(Invitation).to have_received(:deliver) - ().deliver(*(any args)) + (Invitation (class)).deliver(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments """ @@ -116,14 +116,14 @@ Feature: Spies """ When I run `rspec setting_constraints_spec.rb --order defined` Then it should fail with the following output: - | 4 examples, 2 failures | - | | - | 1) An invitiation fails when a count constraint is not satisfied | - | Failure/Error: expect(invitation).to have_received(:deliver).at_least(3).times | - | (Double "invitation").deliver(*(any args)) | - | expected: at least 3 times with any arguments | - | received: 2 times with any arguments | - | | - | 2) An invitiation fails when an order constraint is not satisifed | - | Failure/Error: expect(invitation).to have_received(:deliver).with("foo@example.com").ordered | - | Double "invitation" received :deliver out of order | + | 4 examples, 2 failures | + | | + | 1) An invitiation fails when a count constraint is not satisfied | + | Failure/Error: expect(invitation).to have_received(:deliver).at_least(3).times | + | (Double "invitation").deliver(*(any args)) | + | expected: at least 3 times with any arguments | + | received: 2 times with any arguments | + | | + | 2) An invitiation fails when an order constraint is not satisifed | + | Failure/Error: expect(invitation).to have_received(:deliver).with("foo@example.com").ordered | + | # received :deliver out of order | diff --git a/features/basics/test_doubles.feature b/features/basics/test_doubles.feature index b0cc417ec..59f45fcd9 100644 --- a/features/basics/test_doubles.feature +++ b/features/basics/test_doubles.feature @@ -23,7 +23,7 @@ Feature: Test Doubles When I run `rspec double_spec.rb` Then it should fail with: """ - Double "Some Collaborator" received unexpected message :foo with (no args) + # received unexpected message :foo with (no args) """ Scenario: A hash can be used to define allowed messages and return values diff --git a/features/configuring_responses/yielding.feature b/features/configuring_responses/yielding.feature index 303e5ef49..5679d0074 100644 --- a/features/configuring_responses/yielding.feature +++ b/features/configuring_responses/yielding.feature @@ -37,7 +37,7 @@ Feature: Yielding When I run `rspec no_caller_block_spec.rb` Then it should fail with: """ - Double asked to yield |[2, 3]| but no block was passed + # asked to yield |[2, 3]| but no block was passed """ Scenario: It fails when the caller's block does not accept the provided arguments @@ -54,7 +54,7 @@ Feature: Yielding When I run `rspec arg_mismatch_spec.rb` Then it should fail with: """ - Double yielded |2, 3| to block with arity of 1 + # yielded |2, 3| to block with arity of 1 """ Scenario: Yield multiple times diff --git a/features/outside_rspec/minitest.feature b/features/outside_rspec/minitest.feature index 8e43bd779..0a5d9c856 100644 --- a/features/outside_rspec/minitest.feature +++ b/features/outside_rspec/minitest.feature @@ -65,16 +65,16 @@ Feature: Integrate with Minitest """ When I run `ruby -Itest test/rspec_mocks_test.rb` Then it should fail with the following output: - | 1) Error: | - | RSpecMocksTest#test_failing_negative_expectation: | - | RSpec::Mocks::MockExpectationError: (Double).message(no args) | - | expected: 0 times with any arguments | - | received: 1 time | - | | - | 2) Error: | - | RSpecMocksTest#test_failing_positive_expectation: | - | RSpec::Mocks::MockExpectationError: (Double).message(*(any args)) | - | expected: 1 time with any arguments | - | received: 0 times with any arguments | - | | - | 4 runs, 0 assertions, 0 failures, 2 errors, 0 skips | + | 1) Error: | + | RSpecMocksTest#test_failing_negative_expectation: | + | RSpec::Mocks::MockExpectationError: (Double (anonymous)).message(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 2) Error: | + | RSpecMocksTest#test_failing_positive_expectation: | + | RSpec::Mocks::MockExpectationError: (Double (anonymous)).message(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 4 runs, 0 assertions, 0 failures, 2 errors, 0 skips | diff --git a/features/setting_constraints/matching_arguments.feature b/features/setting_constraints/matching_arguments.feature index 0eb6f9b5f..abc67b265 100644 --- a/features/setting_constraints/matching_arguments.feature +++ b/features/setting_constraints/matching_arguments.feature @@ -39,12 +39,12 @@ Feature: Matching arguments """ When I run `rspec basic_example_spec.rb` Then it should fail with the following output: - | 2 examples, 1 failure | - | | - | Failure/Error: dbl.foo(1, nil, "other") | - | Double received :foo with unexpected arguments | - | expected: (1, anything, /bar/) | - | got: (1, nil, "other") | + | 2 examples, 1 failure | + | | + | Failure/Error: dbl.foo(1, nil, "other") | + | # received :foo with unexpected arguments | + | expected: (1, anything, /bar/) | + | got: (1, nil, "other") | Scenario: Using a RSpec matcher Given a file named "rspec_matcher_spec.rb" with: @@ -64,12 +64,12 @@ Feature: Matching arguments """ When I run `rspec rspec_matcher_spec.rb` Then it should fail with the following output: - | 2 examples, 1 failure | - | | - | Failure/Error: dbl.foo([1, 3]) | - | Double received :foo with unexpected arguments | - | expected: (a collection containing exactly 1 and 2) | - | got: ([1, 3]) | + | 2 examples, 1 failure | + | | + | Failure/Error: dbl.foo([1, 3]) | + | # received :foo with unexpected arguments | + | expected: (a collection containing exactly 1 and 2) | + | got: ([1, 3]) | Scenario: Using a custom matcher Given a file named "custom_matcher_spec.rb" with: @@ -93,12 +93,12 @@ Feature: Matching arguments """ When I run `rspec custom_matcher_spec.rb` Then it should fail with the following output: - | 2 examples, 1 failure | - | | - | Failure/Error: dbl.foo(13) | - | Double received :foo with unexpected arguments | - | expected: (a multiple of 3) | - | got: (13) | + | 2 examples, 1 failure | + | | + | Failure/Error: dbl.foo(13) | + | # received :foo with unexpected arguments | + | expected: (a multiple of 3) | + | got: (13) | Scenario: Responding differently based on the arguments Given a file named "responding_differently_spec.rb" with: diff --git a/features/setting_constraints/message_order.feature b/features/setting_constraints/message_order.feature index 11dc22bae..1e70dffab 100644 --- a/features/setting_constraints/message_order.feature +++ b/features/setting_constraints/message_order.feature @@ -56,8 +56,8 @@ Feature: Message Order Then the examples should all fail, producing the following output: | 1) Constraining order fails when messages are received out of order on one collaborator | | Failure/Error: collaborator_1.step_2 | - | Double "Collaborator 1" received :step_2 out of order | + | # received :step_2 out of order | | | | 2) Constraining order fails when messages are received out of order between collaborators | | Failure/Error: collaborator_2.step_2 | - | Double "Collaborator 2" received :step_2 out of order | + | # received :step_2 out of order | From 1a7cafe4f5ce700d1ddbb5b12a5afcac3dbedf22 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 17 Apr 2015 08:21:52 -0700 Subject: [PATCH 0884/1179] 1.9.2 is having problems with `object_double(:foo)`. --- spec/rspec/mocks/formatting_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/rspec/mocks/formatting_spec.rb b/spec/rspec/mocks/formatting_spec.rb index c1ee58d3a..a08b1776e 100644 --- a/spec/rspec/mocks/formatting_spec.rb +++ b/spec/rspec/mocks/formatting_spec.rb @@ -76,16 +76,16 @@ def actual_formatting(double) end end - describe "`object_double(:foo)`" do + describe "`object_double([])`" do context "with a name" do - specify '#' do - expect(object_double(:foo, "Name")).to format_in_failures_as('#') + specify '#' do + expect(object_double([], "Name")).to format_in_failures_as('#') end end context "without a name" do - specify '#' do - expect(object_double(:foo)).to format_in_failures_as('#') + specify '#' do + expect(object_double([])).to format_in_failures_as('#') end end end From cb1399292d4308bdfd8c30aa08a7a4fc9b00b37c Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Wed, 22 Apr 2015 16:35:47 -0400 Subject: [PATCH 0885/1179] Fix typo in receive example description. --- spec/rspec/mocks/matchers/receive_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index bb66cefb1..2e3027ec4 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -175,7 +175,7 @@ module Mocks end shared_examples "an expect syntax negative expectation" do - it 'sets up a negaive message expectation that passes if the message is not received' do + it 'sets up a negative message expectation that passes if the message is not received' do wrapped.not_to receive(:foo) verify_all end From d0790940bd7cb3cefbf660c89141284fa5b19f0c Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Wed, 29 Apr 2015 18:23:35 -0400 Subject: [PATCH 0886/1179] Benchmark and optimise object allocations for a simple mocking case --- benchmarks/allocations/1_object_1000_mocks.rb | 76 +++++++++++++++++++ benchmarks/allocations/helper.rb | 17 +++++ lib/rspec/mocks/message_expectation.rb | 10 ++- lib/rspec/mocks/method_double.rb | 4 +- lib/rspec/mocks/proxy.rb | 4 +- 5 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 benchmarks/allocations/1_object_1000_mocks.rb create mode 100644 benchmarks/allocations/helper.rb diff --git a/benchmarks/allocations/1_object_1000_mocks.rb b/benchmarks/allocations/1_object_1000_mocks.rb new file mode 100644 index 000000000..27c0ab60d --- /dev/null +++ b/benchmarks/allocations/1_object_1000_mocks.rb @@ -0,0 +1,76 @@ +require_relative "helper" +symbols = (1..1000).map {|x| :"#{x}"} + +benchmark_allocations do + o = Object.new + symbols.each do |sym| + expect(o).to receive(sym) + end + + symbols.each do |sym| + o.send(sym) + end + + RSpec::Mocks.space.verify_all + RSpec::Mocks.space.reset_all +end + +__END__ +As of commit 9ee3e3adb529113bf1cc75cc4424d014f880dc47: + + class_plus count +---------------------------------------------------- ----- +Array 22003 +Proc 3001 +RubyVM::Env 3001 +Array 2000 +String 2000 +Hash 1002 +RSpec::Mocks::ExpectationTarget 1000 +Enumerator 1000 +RSpec::Mocks::Matchers::Receive 1000 +Array 1000 +RSpec::Mocks::InstanceMethodStasher 1000 +RSpec::Mocks::MethodDouble 1000 +Array 1000 +RSpec::Mocks::MessageExpectation 1000 +Array 1000 +Array 1000 +Array 1000 +Array 1000 +RSpec::Mocks::Proxy::SpecificMessage 1000 +RSpec::Mocks::Implementation 1000 +Array 1 +RSpec::Mocks::PartialDoubleProxy 1 +RSpec::Mocks::ErrorGenerator 1 +Array 1 + + +After PR #936: + + class_plus count +---------------------------------------------------- ----- +Array 21003 +RubyVM::Env 3001 +Proc 3001 +String 2000 +RSpec::Mocks::InstanceMethodStasher 1000 +RSpec::Mocks::Matchers::Receive 1000 +RSpec::Mocks::ExpectationTarget 1000 +RSpec::Mocks::Implementation 1000 +RSpec::Mocks::MessageExpectation 1000 +RSpec::Mocks::MethodDouble 1000 +Array 1000 +Array 1000 +Array 1000 +Enumerator 1000 +Array 1000 +Array 1000 +RSpec::Mocks::Proxy::SpecificMessage 1000 +Array 1000 +Array 1000 +Hash 2 +RSpec::Mocks::PartialDoubleProxy 1 +Array 1 +RSpec::Mocks::ErrorGenerator 1 +Array 1 diff --git a/benchmarks/allocations/helper.rb b/benchmarks/allocations/helper.rb new file mode 100644 index 000000000..db00a8d17 --- /dev/null +++ b/benchmarks/allocations/helper.rb @@ -0,0 +1,17 @@ +$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__) +require "allocation_stats" +require 'rspec/mocks/standalone' + +def benchmark_allocations(burn: 1) + stats = AllocationStats.new(burn: burn).trace do + yield + end + + columns = if ENV['DETAIL'] + [:sourcefile, :sourceline, :class_plus] + else + [:class_plus] + end + + puts stats.allocations(alias_paths: true).group_by(*columns).from_pwd.sort_by_size.to_text +end diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 3f0725d96..62a9900b4 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -176,6 +176,11 @@ def and_throw(*args) def and_yield(*args, &block) raise_already_invoked_error_if_necessary(__method__) yield @eval_context = Object.new if block + + # Initialize args to yield now that it's being used, see also: comment + # in constructor. + @args_to_yield ||= [] + @args_to_yield << args self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator) self @@ -370,7 +375,10 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @expectation_type = type @ordered = false @at_least = @at_most = @exactly = nil - @args_to_yield = [] + + # Initialized to nil so that we don't allocate an array for every + # mock or stub. See also comment in `and_yield`. + @args_to_yield = nil @failed_fast = nil @eval_context = nil @yield_receiver_to_implementation_block = false diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index d2c0090f5..fa451fb4f 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -45,7 +45,7 @@ class << @object; self; end # @private def configure_method - @original_visibility = [visibility, method_name] + @original_visibility = visibility @method_stasher.stash unless @method_is_proxied define_proxy_method end @@ -101,7 +101,7 @@ def restore_original_visibility return unless @original_visibility && MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name) - object_singleton_class.__send__(*@original_visibility) + object_singleton_class.__send__(@original_visibility, method_name) end # @private diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 61493e412..cc29b2102 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -45,8 +45,10 @@ def original_method_handle_for(_message) nil end + DEFAULT_MESSAGE_EXPECTATION_OPTS = {}.freeze + # @private - def add_message_expectation(method_name, opts={}, &block) + def add_message_expectation(method_name, opts=DEFAULT_MESSAGE_EXPECTATION_OPTS, &block) location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } meth_double = method_double_for(method_name) From 8e32f2ea105cd931a003aad9db9c98443c8f95b6 Mon Sep 17 00:00:00 2001 From: Pavel Shpak Date: Mon, 4 May 2015 00:10:37 +0300 Subject: [PATCH 0887/1179] fix typo for hide_const result printout --- features/mutating_constants/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/mutating_constants/README.md b/features/mutating_constants/README.md index 1d92a8684..a43970785 100644 --- a/features/mutating_constants/README.md +++ b/features/mutating_constants/README.md @@ -66,7 +66,7 @@ restored to its original value after the test completes. ```ruby FOO = 42 hide_const("FOO") -FOO => NameError: uninitialized constant FOO +FOO # => NameError: uninitialized constant FOO ``` Like stubbed constants, names must be fully qualified. From e5d1c458660bf86bda9c4cbba7d501198c5ce190 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 5 May 2015 17:56:26 +1000 Subject: [PATCH 0888/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 5 ++--- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 5a3a5b0ca..53a1a087a 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. +# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 8a9d1e33c..debb1a71a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. +# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 668e41645..8ddd66aa9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. +# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -18,8 +18,7 @@ install: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - ruby --version - gem --version - # We lock to 1.7.7 to avoid warnings from 1.7.8 - - gem install bundler -v=1.7.7 + - gem install bundler - bundler --version - bundle install - cinst ansicon diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index c0bf6b51e..f7eb356e8 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. +# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 30b0b4269..ef0284c38 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. +# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index f4478edf1..d9b9bb8ad 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. +# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 26f1b82f4..91e0b822b 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. +# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 5c3691fb9..9832e7b7c 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-03-15T22:57:16-07:00 from the rspec-dev repo. +# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 006a56ca7d18c35758c36c1791eecc7c458c4863 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 1 May 2015 17:49:48 +1000 Subject: [PATCH 0889/1179] rename callback for verifying doubles --- features/verifying_doubles/dynamic_classes.feature | 4 ++-- lib/rspec/mocks/configuration.rb | 11 ++++++----- lib/rspec/mocks/example_methods.rb | 2 +- spec/rspec/mocks/configuration_spec.rb | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index eaf9cf3f1..c31b32c5c 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -94,14 +94,14 @@ Feature: Dynamic classes And a file named "spec/fake_record_helper.rb" with: """ruby RSpec.configuration.mock_with(:rspec) do |config| - config.when_declaring_verifying_double do |reference| + config.before_verifying_doubles do |reference| reference.target.define_attribute_methods end end # # or you can use: # - # RSpec::Mocks.configuration.when_declaring_verifying_double do |reference| + # RSpec::Mocks.configuration.before_verifying_doubles do |reference| # reference.target.define_attribute_methods # end """ diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 28b22b879..e09a12a5c 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -103,17 +103,18 @@ def verify_doubled_constant_names? # Provides a way to perform customisations when verifying doubles. # # @example - # RSpec::Mocks.configuration.when_declaring_verifying_double do |ref| + # RSpec::Mocks.configuration.before_verifying_doubles do |ref| # ref.some_method! # end - def when_declaring_verifying_double(&block) - verifying_double_declaration_callbacks << block + def before_verifying_doubles(&block) + verifying_double_callbacks << block end + alias :when_declaring_verifying_double :before_verifying_doubles # @api private # Returns an array of blocks to call when verifying doubles - def verifying_double_declaration_callbacks - @verifying_double_declaration_callbacks ||= [] + def verifying_double_callbacks + @verifying_double_callbacks ||= [] end def transfer_nested_constants? diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 04c4ca9b2..b3c07a62a 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -402,7 +402,7 @@ def self.declare_verifying_double(type, ref, *args) "Disable check with `verify_doubled_constant_names` configuration option." end - RSpec::Mocks.configuration.verifying_double_declaration_callbacks.each do |block| + RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| block.call(ref) end diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 3a91cdf85..4308b1220 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -178,9 +178,9 @@ def configure_default_syntax block = proc { |ref| ref } block2 = proc { |ref| ref } RSpec.configuration.mock_with :rspec do |config| - config.when_declaring_verifying_double(&block) + config.before_verifying_doubles(&block) config.when_declaring_verifying_double(&block2) - expect(config.verifying_double_declaration_callbacks).to eq [block, block2] + expect(config.verifying_double_callbacks).to eq [block, block2] end end end From e5bfbcb75bca581924697ab0e9fd9447e9257b76 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 4 May 2015 14:40:19 +1000 Subject: [PATCH 0890/1179] use the verifying double callback when creating any_instance and normal verifying partial doubles --- lib/rspec/mocks/verifying_proxy.rb | 4 ++ spec/rspec/mocks/partial_double_spec.rb | 49 ++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 92845cb16..4f058f2d5 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -98,6 +98,10 @@ def initialize(object, expectation_ordering) @method_doubles = Hash.new do |h, k| h[k] = VerifyingExistingMethodDouble.for(object, k, self) end + + RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| + block.call @doubled_module + end end def method_reference diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index e7f54effc..8372d9199 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -349,6 +349,28 @@ def defined_private_method expect(object.send(:defined_private_method)).to eq("works") end + it 'runs the before_verifying_double callbacks before verifying an expectation' do + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.before_verifying_doubles(&probe) + end + + expect(object).to receive(:implemented) + }.to yield_with_args(have_attributes :target => object) + object.implemented + end + + it 'runs the before_verifying_double callbacks before verifying an allowance' do + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.before_verifying_doubles(&probe) + end + + allow(object).to receive(:implemented) + }.to yield_with_args(have_attributes :target => object) + object.implemented + end + it 'does not allow a non-existing method to be expected' do prevents { expect(object).to receive(:unimplemented) } end @@ -374,11 +396,36 @@ def defined_private_method object.implemented end - it 'allows private methods to be expected on any_instance' do + it 'allows private methods to be expected on any_instance expectation' do expect_any_instance_of(klass).to receive(:defined_private_method).and_call_original object.send(:defined_private_method) end + it 'runs the before_verifying_double callbacks on any_instance before verifying a double allowance' do + expect_any_instance_of(klass).to receive(:implemented) + + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.before_verifying_doubles(&probe) + end + + object.implemented + }.to yield_with_args(have_attributes :target => klass) + end + + it 'runs the before_verifying_double callbacks on any_instance before verifying a double' do + allow_any_instance_of(klass).to receive(:implemented) + + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.before_verifying_doubles(&probe) + end + + object.implemented + }.to yield_with_args(have_attributes :target => klass) + end + + it 'does not allow a non-existing method to be called on any_instance' do prevents(/does not implement/) { expect_any_instance_of(klass).to receive(:unimplemented) From 1826212a66598b171fece2998f68998e4df940e1 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 5 May 2015 18:36:37 +1000 Subject: [PATCH 0891/1179] changelog for #940 --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 43d77e85f..c09d781bd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,9 @@ Enhancements: number of times and the received arguments in the output. (John Ceh, #918) * Improve how test doubles are represented in failure messages. (@sivagollapalli, Myron Marston, #932) +* Rename `RSpec::Mocks::Configuration#when_declaring_verifying_double` to + `RSpec::Mocks::Configuration#before_verifying_doubles` and utilise when + verifying partial doubles. (Jon Rowe, #940) Bug Fixes: From 2e7f675ba8b4453ba015077c4216e037ef1b30b3 Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Sun, 3 May 2015 18:09:43 +0200 Subject: [PATCH 0892/1179] Correct the grammar of a phrase --- features/basics/allowing_messages.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/basics/allowing_messages.feature b/features/basics/allowing_messages.feature index d801f2544..37a8d67ad 100644 --- a/features/basics/allowing_messages.feature +++ b/features/basics/allowing_messages.feature @@ -2,7 +2,7 @@ Feature: Allowing messages [Test doubles](./test-doubles) are "strict" by default -- messages that have not been specifically allowed or expected will trigger an error. Use `allow(...).to receive(...)` to configure - which messages are the double is allowed to receive. You can also use `allow(...).to + which messages the double is allowed to receive. You can also use `allow(...).to receive_messages(...)` to configure allowed messages (and return values) in bulk. Scenario: Allowed messages return nil by default From 510ca9fea7bb244f6eb2c9da4629a5069361abdf Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Sun, 3 May 2015 18:22:36 +0200 Subject: [PATCH 0893/1179] Remove a hyphen --- features/basics/partial_test_doubles.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/basics/partial_test_doubles.feature b/features/basics/partial_test_doubles.feature index 2b6261f3e..be1896b0b 100644 --- a/features/basics/partial_test_doubles.feature +++ b/features/basics/partial_test_doubles.feature @@ -19,7 +19,7 @@ Feature: Partial test doubles expect(Person).to receive(:find) { person } ``` - RSpec replaces the method we're stubbing or mocking with its own test-double-like method. + RSpec replaces the method we're stubbing or mocking with its own test-double like method. At the end of the example, RSpec verifies any message expectations, and then restores the original methods. From e598f15cc08653d2f14e7e08ed0b6c8e3f9e4dfa Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Mon, 4 May 2015 00:03:47 +0200 Subject: [PATCH 0894/1179] Increase line length to fix an odd-looking (too short) line on Relish The hyperlink to `object_double` in the Markdown source makes the line longer than it is when rendered on Relish, and the line break after "work if you" thus makes the line look too short compared to others on Relish. --- features/verifying_doubles/dynamic_classes.feature | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/features/verifying_doubles/dynamic_classes.feature b/features/verifying_doubles/dynamic_classes.feature index c31b32c5c..8f6ae2bbf 100644 --- a/features/verifying_doubles/dynamic_classes.feature +++ b/features/verifying_doubles/dynamic_classes.feature @@ -5,9 +5,8 @@ Feature: Dynamic classes the case when `method_missing` is used. There are a few ways to work around this. If the object has already been loaded you may - consider using an [`object_double`](./using-an-object-double), but that cannot work if you - are testing in isolation. Alternatively you could implement the methods directly (calling - `super` to return the `method_missing` definition). + consider using an [`object_double`](./using-an-object-double), but that cannot work if you are testing in isolation. + Alternatively you could implement the methods directly (calling `super` to return the `method_missing` definition). Some of these classes may have methods to define these methods on the objects at runtime. (For example, `ActiveRecord` does this to define methods from database columns.) For these From 4ef239a998264ab15dc8e333c9a0fb0bfd3f090d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 13 May 2015 00:01:47 -0700 Subject: [PATCH 0895/1179] Remove unused method. --- lib/rspec/mocks/message_chain.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/rspec/mocks/message_chain.rb b/lib/rspec/mocks/message_chain.rb index 4106cb2ec..907d14b0e 100644 --- a/lib/rspec/mocks/message_chain.rb +++ b/lib/rspec/mocks/message_chain.rb @@ -30,10 +30,6 @@ def setup_chain private - def expectation(_object, _message, &_return_block) - raise NotImplementedError - end - def chain_on(object, *chain, &block) initialize(object, *chain, &block) setup_chain From 91d8960107199df2f21a58112cb4a165248858f5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 12 May 2015 17:09:31 -0700 Subject: [PATCH 0896/1179] Use error generator for `any_instance` errors. Fixes #646. --- lib/rspec/mocks/any_instance.rb | 1 + lib/rspec/mocks/any_instance/chain.rb | 2 +- .../mocks/any_instance/error_generator.rb | 35 +++++++++++++++++++ .../mocks/any_instance/message_chains.rb | 4 +-- lib/rspec/mocks/any_instance/recorder.rb | 17 ++++----- lib/rspec/mocks/error_generator.rb | 13 +++---- lib/rspec/mocks/message_expectation.rb | 2 +- 7 files changed, 53 insertions(+), 21 deletions(-) create mode 100644 lib/rspec/mocks/any_instance/error_generator.rb diff --git a/lib/rspec/mocks/any_instance.rb b/lib/rspec/mocks/any_instance.rb index bc912d84b..41eae8146 100644 --- a/lib/rspec/mocks/any_instance.rb +++ b/lib/rspec/mocks/any_instance.rb @@ -1,5 +1,6 @@ %w[ any_instance/chain + any_instance/error_generator any_instance/stub_chain any_instance/stub_chain_chain any_instance/expect_chain_chain diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index db9d6184b..c9ce232af 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -76,7 +76,7 @@ def expectation_fulfilled! end def never - ErrorGenerator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated? + AnyInstance.error_generator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated? super end diff --git a/lib/rspec/mocks/any_instance/error_generator.rb b/lib/rspec/mocks/any_instance/error_generator.rb new file mode 100644 index 000000000..eae62ff90 --- /dev/null +++ b/lib/rspec/mocks/any_instance/error_generator.rb @@ -0,0 +1,35 @@ +module RSpec + module Mocks + module AnyInstance + # @private + class ErrorGenerator < ::RSpec::Mocks::ErrorGenerator + def raise_second_instance_received_message_error(unfulfilled_expectations) + __raise "Exactly one instance should have received the following " \ + "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" + end + + def raise_method_cant_be_unstubbed_error(method_name) + __raise "The method `#{method_name}` was not stubbed or was already unstubbed" + end + + def raise_does_not_implement_error(klass, method_name) + __raise "#{klass} does not implement ##{method_name}" + end + + def raise_message_already_received_by_other_instance_error(method_name, object_inspect, invoked_instance) + __raise "The message '#{method_name}' was received by #{object_inspect} " \ + "but has already been received by #{invoked_instance}" + end + + def raise_not_supported_with_prepend_error(method_name, problem_mod) + __raise "Using `any_instance` to stub a method (#{method_name}) that has been " \ + "defined on a prepended module (#{problem_mod}) is not supported." + end + end + + def self.error_generator + @error_generator ||= ErrorGenerator.new + end + end + end +end diff --git a/lib/rspec/mocks/any_instance/message_chains.rb b/lib/rspec/mocks/any_instance/message_chains.rb index 0ce39bc8d..7298643d5 100644 --- a/lib/rspec/mocks/any_instance/message_chains.rb +++ b/lib/rspec/mocks/any_instance/message_chains.rb @@ -75,9 +75,7 @@ def raise_if_second_instance_to_receive_message(instance) return unless ExpectationChain === instance return if @instance_with_expectation.equal?(instance) - raise RSpec::Mocks::MockExpectationError, - "Exactly one instance should have received the following " \ - "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" + AnyInstance.error_generator.raise_second_instance_received_message_error(unfulfilled_expectations) end end end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 271348b4a..486dc991d 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -76,7 +76,7 @@ def should_not_receive(method_name, &block) # @see Methods#unstub def unstub(method_name) unless @observed_methods.include?(method_name.to_sym) - raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" + AnyInstance.error_generator.raise_method_cant_be_unstubbed_error(method_name) end message_chains.remove_stub_chains_for!(method_name) stubs[method_name].clear @@ -91,9 +91,7 @@ def verify return unless @expectation_set return if message_chains.all_expectations_fulfilled? - raise RSpec::Mocks::MockExpectationError, - "Exactly one instance should have received the following " \ - "message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}" + AnyInstance.error_generator.raise_second_instance_received_message_error(message_chains.unfulfilled_expectations) end # @private @@ -221,8 +219,7 @@ def observe!(method_name) if RSpec::Mocks.configuration.verify_partial_doubles? unless public_protected_or_private_method_defined?(method_name) - raise MockExpectationError, - "#{@klass} does not implement ##{method_name}" + AnyInstance.error_generator.raise_does_not_implement_error(@klass, method_name) end end @@ -242,7 +239,9 @@ def mark_invoked!(method_name) @klass.__send__(:define_method, method_name) do |*_args, &_blk| invoked_instance = recorder.instance_that_received(method_name) inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>" - raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{inspect} but has already been received by #{invoked_instance}" + AnyInstance.error_generator.raise_message_already_received_by_other_instance_error( + method_name, inspect, invoked_instance + ) end end @@ -252,9 +251,7 @@ def allow_no_prepended_module_definition_of(method_name) problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) } return unless problem_mod - raise RSpec::Mocks::MockExpectationError, - "Using `any_instance` to stub a method (#{method_name}) that has been " \ - "defined on a prepended module (#{problem_mod}) is not supported." + AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod) end else def allow_no_prepended_module_definition_of(_method_name) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index d2f113235..767f7d44d 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -34,7 +34,7 @@ module Mocks class ErrorGenerator attr_writer :opts - def initialize(target) + def initialize(target=nil) @target = target end @@ -167,11 +167,12 @@ def raise_expectation_on_mocked_method(method) "method has been mocked instead of stubbed or spied." end - def self.raise_double_negation_error(wrapped_expression) - raise "Isn't life confusing enough? You've already set a " \ - "negative message expectation and now you are trying to " \ - "negate it again with `never`. What does an expression like " \ - "`#{wrapped_expression}.not_to receive(:msg).never` even mean?" + # @private + def raise_double_negation_error(wrapped_expression) + __raise "Isn't life confusing enough? You've already set a " \ + "negative message expectation and now you are trying to " \ + "negate it again with `never`. What does an expression like " \ + "`#{wrapped_expression}.not_to receive(:msg).never` even mean?" end private diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 62a9900b4..e4ef0d5c2 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -252,7 +252,7 @@ def times(&block) # @example # expect(car).to receive(:stop).never def never - ErrorGenerator.raise_double_negation_error("expect(obj)") if negative? + error_generator.raise_double_negation_error("expect(obj)") if negative? @expected_received_count = 0 self end From 4f37fb5bc2f569b10ca277964007fe409732b52f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 12 May 2015 20:40:51 -0700 Subject: [PATCH 0897/1179] Refactor: convert more errors to use error generator. --- .../mocks/any_instance/error_generator.rb | 4 -- lib/rspec/mocks/any_instance/recorder.rb | 2 +- lib/rspec/mocks/error_generator.rb | 45 ++++++++++++++++++- lib/rspec/mocks/example_methods.rb | 5 +-- lib/rspec/mocks/matchers/have_received.rb | 7 +-- lib/rspec/mocks/message_expectation.rb | 6 +-- lib/rspec/mocks/method_double.rb | 2 +- 7 files changed, 50 insertions(+), 21 deletions(-) diff --git a/lib/rspec/mocks/any_instance/error_generator.rb b/lib/rspec/mocks/any_instance/error_generator.rb index eae62ff90..d1046cb0b 100644 --- a/lib/rspec/mocks/any_instance/error_generator.rb +++ b/lib/rspec/mocks/any_instance/error_generator.rb @@ -8,10 +8,6 @@ def raise_second_instance_received_message_error(unfulfilled_expectations) "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" end - def raise_method_cant_be_unstubbed_error(method_name) - __raise "The method `#{method_name}` was not stubbed or was already unstubbed" - end - def raise_does_not_implement_error(klass, method_name) __raise "#{klass} does not implement ##{method_name}" end diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 486dc991d..943728ad5 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -76,7 +76,7 @@ def should_not_receive(method_name, &block) # @see Methods#unstub def unstub(method_name) unless @observed_methods.include?(method_name.to_sym) - AnyInstance.error_generator.raise_method_cant_be_unstubbed_error(method_name) + AnyInstance.error_generator.raise_method_not_stubbed_error(method_name) end message_chains.remove_stub_chains_for!(method_name) stubs[method_name].clear diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 767f7d44d..6e2950756 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -175,6 +175,40 @@ def raise_double_negation_error(wrapped_expression) "`#{wrapped_expression}.not_to receive(:msg).never` even mean?" end + # @private + def raise_verifying_double_not_defined_error(ref) + notify(VerifyingDoubleNotDefinedError.new( + "#{ref.description.inspect} is not a defined constant. " \ + "Perhaps you misspelt it? " \ + "Disable check with `verify_doubled_constant_names` configuration option." + )) + end + + # @private + def raise_have_received_disallowed(type, reason) + __raise "Using #{type}(...) with the `have_received` " \ + "matcher is not supported#{reason}." + end + + # @private + def raise_cant_constrain_count_for_negated_have_received_error(count_constraint) + __raise "can't use #{count_constraint} when negative" + end + + # @private + def raise_method_not_stubbed_error(method_name) + __raise "The method `#{method_name}` was not stubbed or was already unstubbed" + end + + # @private + def raise_already_invoked_error(message, calling_customization) + error_message = "The message expectation for #{intro}.#{message} has already been invoked " \ + "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \ + "customizations must be applied before it is used for the first time." + + notify MockExpectationAlreadyInvokedError.new(error_message) + end + private def received_part_of_expectation_error(actual_received_count, args) @@ -261,7 +295,11 @@ def intro(unwrapped=false) def __raise(message) message = opts[:message] unless opts[:message].nil? - Kernel.raise(RSpec::Mocks::MockExpectationError, message) + notify RSpec::Mocks::MockExpectationError.new(message) + end + + def notify(exception) + Kernel.raise(exception) end def format_args(args) @@ -301,5 +339,10 @@ def group_count(index, args) " (#{times(index)})" if args.size > 1 || index > 1 end end + + # @private + def self.error_generator + @error_generator ||= ErrorGenerator.new + end end end diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index b3c07a62a..4de71a2f6 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -396,10 +396,7 @@ def self.declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? - raise VerifyingDoubleNotDefinedError, - "#{ref.description.inspect} is not a defined constant. " \ - "Perhaps you misspelt it? " \ - "Disable check with `verify_doubled_constant_names` configuration option." + RSpec::Mocks.error_generator.raise_verifying_double_not_defined_error(ref) end RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index b7611c632..d227270c2 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -69,9 +69,7 @@ def setup_any_instance_expectation(_subject, &_block) private def disallow(type, reason="") - raise RSpec::Mocks::MockExpectationError, - "Using #{type}(...) with the `have_received` " \ - "matcher is not supported#{reason}." + RSpec::Mocks.error_generator.raise_have_received_disallowed(type, reason) end def expect @@ -88,8 +86,7 @@ def apply_constraints_to(expectation) def ensure_count_unconstrained return unless count_constraint - raise RSpec::Mocks::MockExpectationError, - "can't use #{count_constraint} when negative" + RSpec::Mocks.error_generator.raise_cant_constrain_count_for_negated_have_received_error(count_constraint) end def count_constraint diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index e4ef0d5c2..0b9951aa4 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -553,11 +553,7 @@ def has_been_invoked? def raise_already_invoked_error_if_necessary(calling_customization) return unless has_been_invoked? - error_message = "The message expectation for #{orig_object.inspect}.#{message} has already been invoked " \ - "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \ - "customizations must be applied before it is used for the first time." - - raise MockExpectationAlreadyInvokedError, error_message + error_generator.raise_already_invoked_error(message, calling_customization) end def failed_fast? diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index fa451fb4f..850a9c156 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -198,7 +198,7 @@ def remove_stub_if_present # @private def raise_method_not_stubbed_error - raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" + RSpec::Mocks.error_generator.raise_method_not_stubbed_error(method_name) end # In Ruby 2.0.0 and above prepend will alter the method lookup chain. From 879eb4f941fb11584686f8507e6c138b45e6b91a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 13 May 2015 00:23:03 -0700 Subject: [PATCH 0898/1179] Use the rspec-support failure notifier for mock expectation failures. For rspec/rspec-expectations#733. --- lib/rspec/mocks/error_generator.rb | 2 +- lib/rspec/mocks/message_expectation.rb | 8 ++++--- spec/rspec/mocks/failure_notification_spec.rb | 23 +++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 spec/rspec/mocks/failure_notification_spec.rb diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 6e2950756..349bf5d3a 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -299,7 +299,7 @@ def __raise(message) end def notify(exception) - Kernel.raise(exception) + RSpec::Support.notify_failure(exception) end def format_args(args) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 0b9951aa4..69a30c5ce 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -709,11 +709,13 @@ def cannot_modify_further_error # # @private class InsertOntoBacktrace - def self.line(location) - yield + RAISE_METHOD = method(:raise) + + def self.line(location, &block) + RSpec::Support.with_failure_notifier(RAISE_METHOD, &block) rescue RSpec::Mocks::MockExpectationError => error error.backtrace.insert(0, location) - Kernel.raise error + RSpec::Support.notify_failure(error) end end end diff --git a/spec/rspec/mocks/failure_notification_spec.rb b/spec/rspec/mocks/failure_notification_spec.rb new file mode 100644 index 000000000..cd282ee60 --- /dev/null +++ b/spec/rspec/mocks/failure_notification_spec.rb @@ -0,0 +1,23 @@ +RSpec.describe "Failure notification" do + def capture_errors(&block) + errors = [] + RSpec::Support.with_failure_notifier(lambda { |e| errors << e }, &block) + errors + end + + it "uses the rspec-support notifier to support `aggregate_failures`" do + dbl = double("Foo") + + expect(capture_errors { dbl.some_unallowed_method }).to match [an_object_having_attributes( + :message => a_string_including(dbl.inspect, "some_unallowed_method") + )] + end + + it "includes the line of future expectation in the notification for an unreceived message" do + dbl = double("Foo") + expect(dbl).to receive(:wont_happen); expected_from_line = __LINE__ + + error = capture_errors { verify dbl }.first + expect(error.backtrace.first).to match(/#{File.basename(__FILE__)}:#{expected_from_line}/) + end +end From 0e7052c5d7f7e42f24defcb3bea91e7d7b891592 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 20 May 2015 22:29:07 -0700 Subject: [PATCH 0899/1179] Make `have_received` compatible with `aggregate_failures`. Fixes #952. --- lib/rspec/mocks/matchers/have_received.rb | 8 +++--- spec/rspec/mocks/failure_notification_spec.rb | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index d227270c2..27b890fdc 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -96,10 +96,10 @@ def count_constraint end def generate_failure_message - mock_proxy.check_for_unexpected_arguments(@expectation) - @expectation.generate_error - rescue RSpec::Mocks::MockExpectationError => error - error.message + RSpec::Support.with_failure_notifier(Proc.new { |e| return e.message }) do + mock_proxy.check_for_unexpected_arguments(@expectation) + @expectation.generate_error + end end def expected_messages_received_in_order? diff --git a/spec/rspec/mocks/failure_notification_spec.rb b/spec/rspec/mocks/failure_notification_spec.rb index cd282ee60..3b0081f21 100644 --- a/spec/rspec/mocks/failure_notification_spec.rb +++ b/spec/rspec/mocks/failure_notification_spec.rb @@ -20,4 +20,30 @@ def capture_errors(&block) error = capture_errors { verify dbl }.first expect(error.backtrace.first).to match(/#{File.basename(__FILE__)}:#{expected_from_line}/) end + + context "when using `aggregate_failures`" do + specify 'spy failures for unreceived messages are reported correctly' do + expect { + aggregate_failures do + expect(spy).to have_received(:foo) + end + }.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |e| + expect(e).not_to be_a(RSpec::Expectations::MultipleExpectationsNotMetError) + expect(e.message).to include("expected: 1 time", "received: 0 times") + end + end + + specify 'spy failures for messages received with unexpected args are reported correctly' do + expect { + aggregate_failures do + the_spy = spy + the_spy.foo(1) + expect(the_spy).to have_received(:foo).with(2) + end + }.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |e| + expect(e).not_to be_a(RSpec::Expectations::MultipleExpectationsNotMetError) + expect(e.message).to include("expected: (2)", "got: (1)") + end + end + end end From de4f78cf996b4a05d909c448680b255c00060ecc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 21 May 2015 00:14:07 -0700 Subject: [PATCH 0900/1179] Distinguish between the original method and a method missing proc. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before, we would return a proc that invokes `method_missing` from `original_method`, but it wasn’t a `Method` object and couldn’t be bound, leading to `NoMethodError` in an edge case. Fixes #951. --- Changelog.md | 2 ++ lib/rspec/mocks/message_expectation.rb | 2 +- lib/rspec/mocks/method_double.rb | 16 ++++++++++------ lib/rspec/mocks/verifying_proxy.rb | 4 ++-- spec/rspec/mocks/partial_double_spec.rb | 12 ++++++++++++ 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Changelog.md b/Changelog.md index c09d781bd..2df70afad 100644 --- a/Changelog.md +++ b/Changelog.md @@ -34,6 +34,8 @@ Bug Fixes: * Fix `any_args`/`anything` support so that we avoid calling `obj == anything` on user objects that may have improperly implemented `==` in a way that raises errors. (Myron Marston, #924) +* Fix edge case involving stubbing the same method on a class and a subclass + which previously hit a `NoMethodError` internally in RSpec. (Myron Marston #954) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 69a30c5ce..c17f1f793 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -118,7 +118,7 @@ def and_wrap_original(&block) @error_generator.raise_only_valid_on_a_partial_double(:and_call_original) else warn_about_stub_override if implementation.inner_action - @implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block) + @implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block) @yield_receiver_to_implementation_block = false end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 850a9c156..53e6efd7a 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -18,20 +18,24 @@ def initialize(object, method_name, proxy) @stubs = [] end - def original_method + def original_implementation_callable # If original method is not present, uses the `method_missing` # handler of the object. This accounts for cases where the user has not # correctly defined `respond_to?`, and also 1.8 which does not provide # method handles for missing methods even if `respond_to?` is correct. - @original_method ||= - @method_stasher.original_method || - @proxy.original_method_handle_for(method_name) || + @original_implementation_callable ||= original_method || Proc.new do |*args, &block| @object.__send__(:method_missing, @method_name, *args, &block) end end - alias_method :save_original_method!, :original_method + alias_method :save_original_implementation_callable!, :original_implementation_callable + + def original_method + @original_method ||= + @method_stasher.original_method || + @proxy.original_method_handle_for(method_name) + end # @private def visibility @@ -54,7 +58,7 @@ def configure_method def define_proxy_method return if @method_is_proxied - save_original_method! + save_original_implementation_callable! definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility| define_method(method_name) do |*args, &block| method_double.proxy_method_invoked(self, *args, &block) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 4f058f2d5..5bb331929 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -163,11 +163,11 @@ def initialize(object, method_name, proxy) # Trigger an eager find of the original method since if we find it any # later we end up getting a stubbed method with incorrect arity. - save_original_method! + save_original_implementation_callable! end def with_signature - yield Support::MethodSignature.new(original_method) + yield Support::MethodSignature.new(original_implementation_callable) end def unimplemented? diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 8372d9199..b96932e2c 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -74,6 +74,18 @@ module Mocks object.foobar end + it 'allows a class and a subclass to both be stubbed' do + pending "Does not work on 1.8.7 due to singleton method restrictions" if RUBY_VERSION == "1.8.7" && RSpec::Support::Ruby.mri? + the_klass = Class.new + the_subklass = Class.new(the_klass) + + allow(the_klass).to receive(:foo).and_return(1) + allow(the_subklass).to receive(:foo).and_return(2) + + expect(the_klass.foo).to eq(1) + expect(the_subklass.foo).to eq(2) + end + it "verifies the method was called when expecting a message" do expect(object).to receive(:foobar).with(:test_param).and_return(1) expect { From c54bcf3fe9a5e7a46ba554de223be540c1b5bc17 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 May 2015 23:00:32 -0700 Subject: [PATCH 0901/1179] Ignore spec/examples.txt --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 685984205..5004e6d66 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ Gemfile-custom bundle .rbx .rspec-local +spec/examples.txt From 1449556453bfaddcd3641777112165179ab766ba Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 22 May 2015 23:06:32 -0700 Subject: [PATCH 0902/1179] Use rspec-support's ObjectFormatter to format arguments. This ensures are improved formatting for things like time objects and matchers is used. Fixes #898. --- Changelog.md | 3 +++ lib/rspec/mocks.rb | 1 - lib/rspec/mocks/error_generator.rb | 8 +++----- spec/rspec/mocks/error_generator_spec.rb | 10 ++++++++++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2df70afad..afd830dae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,9 @@ Enhancements: * Rename `RSpec::Mocks::Configuration#when_declaring_verifying_double` to `RSpec::Mocks::Configuration#before_verifying_doubles` and utilise when verifying partial doubles. (Jon Rowe, #940) +* Use rspec-support's `ObjectFormatter` for improved formatting of + arguments in failure messages so that, for example, full time + precisions is displayed for time objets. (Gavin Miller, Myron Marston, #955) Bug Fixes: diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 243905595..17b2831bc 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -2,7 +2,6 @@ RSpec::Support.require_rspec_support 'caller_filter' RSpec::Support.require_rspec_support 'warnings' RSpec::Support.require_rspec_support 'ruby_features' -RSpec::Support.require_rspec_support 'object_inspector' RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f } diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 349bf5d3a..4cea8c0a6 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -1,3 +1,5 @@ +RSpec::Support.require_rspec_support "object_formatter" + module RSpec module Mocks # Raised when a message expectation is not satisfied. @@ -308,11 +310,7 @@ def format_args(args) end def arg_list(args) - args.map { |arg| arg_has_valid_description?(arg) ? arg.description : arg.inspect }.join(", ") - end - - def arg_has_valid_description?(arg) - RSpec::Support.is_a_matcher?(arg) && arg.respond_to?(:description) + args.map { |arg| RSpec::Support::ObjectFormatter.format(arg) }.join(", ") end def format_received_args(args_for_multiple_calls) diff --git a/spec/rspec/mocks/error_generator_spec.rb b/spec/rspec/mocks/error_generator_spec.rb index 07c712561..373291488 100644 --- a/spec/rspec/mocks/error_generator_spec.rb +++ b/spec/rspec/mocks/error_generator_spec.rb @@ -8,6 +8,16 @@ def unexpected_failure_message_for(object_description) end describe "formatting arguments" do + it 'formats time objects with increased precision' do + time = Time.utc(1969, 12, 31, 19, 01, 40, 101) + expected_output = "1969-12-31 19:01:40.000101" + + o = double(:double) + expect { + o.bees(time) + }.to fail_including(expected_output) + end + context "on non-matcher objects that define #description" do it "does not use the object's description" do o = double(:double, :description => "Friends") From 9c192ced62f017dc33bd39d879b0d79efc964ae0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 May 2015 22:01:12 -0700 Subject: [PATCH 0903/1179] Update changelog. [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index afd830dae..1267678c2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,13 +14,13 @@ Enhancements: * Improve mock expectation failure messages so that it combines both number of times and the received arguments in the output. (John Ceh, #918) * Improve how test doubles are represented in failure messages. - (@sivagollapalli, Myron Marston, #932) + (Siva Gollapalli, Myron Marston, #932) * Rename `RSpec::Mocks::Configuration#when_declaring_verifying_double` to `RSpec::Mocks::Configuration#before_verifying_doubles` and utilise when verifying partial doubles. (Jon Rowe, #940) * Use rspec-support's `ObjectFormatter` for improved formatting of arguments in failure messages so that, for example, full time - precisions is displayed for time objets. (Gavin Miller, Myron Marston, #955) + precisions is displayed for time objects. (Gavin Miller, Myron Marston, #955) Bug Fixes: From bc3d0056337688940bd443ae4539912d3e3064b2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 May 2015 22:16:42 -0700 Subject: [PATCH 0904/1179] Prevent received count from being double-incremented. For #956. --- lib/rspec/mocks/message_expectation.rb | 22 ++++++++++--------- spec/rspec/mocks/failure_notification_spec.rb | 9 ++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index c17f1f793..4d3b3de44 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -526,24 +526,26 @@ def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, args.unshift(orig_object) if yield_receiver_to_implementation_block? if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count)) - @actual_received_count += increment @failed_fast = true # args are the args we actually received, @argument_list_matcher is the # list of args we were expecting - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, args) + @error_generator.raise_expectation_error( + @message, @expected_received_count, + @argument_list_matcher, + @actual_received_count + increment, + expectation_count_type, args + ) end @order_group.handle_order_constraint self - begin - if implementation.present? - implementation.call(*args, &block) - elsif parent_stub - parent_stub.invoke(nil, *args, &block) - end - ensure - @actual_received_count += increment + if implementation.present? + implementation.call(*args, &block) + elsif parent_stub + parent_stub.invoke(nil, *args, &block) end + ensure + @actual_received_count += increment end def has_been_invoked? diff --git a/spec/rspec/mocks/failure_notification_spec.rb b/spec/rspec/mocks/failure_notification_spec.rb index 3b0081f21..5b8a215c2 100644 --- a/spec/rspec/mocks/failure_notification_spec.rb +++ b/spec/rspec/mocks/failure_notification_spec.rb @@ -21,6 +21,15 @@ def capture_errors(&block) expect(error.backtrace.first).to match(/#{File.basename(__FILE__)}:#{expected_from_line}/) end + it "does not allow a double to miscount the number of times a message was recevied when a failure is notified in an alternate way" do + dbl = double("Foo") + expect(dbl).not_to receive(:bar) + + capture_errors { dbl.bar } + + expect { verify dbl }.to fail_including("expected: 0 times", "received: 1 time") + end + context "when using `aggregate_failures`" do specify 'spy failures for unreceived messages are reported correctly' do expect { From f82c024d0de02fbb25df3b7a29ce1e356b05a013 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 28 May 2015 08:49:12 +1000 Subject: [PATCH 0905/1179] Changelog for #957 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 1267678c2..545f91012 100644 --- a/Changelog.md +++ b/Changelog.md @@ -39,6 +39,8 @@ Bug Fixes: raises errors. (Myron Marston, #924) * Fix edge case involving stubbing the same method on a class and a subclass which previously hit a `NoMethodError` internally in RSpec. (Myron Marston #954) +* Fix edge case where the message received count would be incremented multiple + times for one failure. (Myron Marston, #957) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) From e94ce3e850f6a75dfe8b6e8dc7fbe85db59bbb02 Mon Sep 17 00:00:00 2001 From: takiy33 Date: Fri, 29 May 2015 01:58:29 +0900 Subject: [PATCH 0906/1179] Removed deprecated option from .gemspec --- rspec-mocks.gemspec | 2 -- 1 file changed, 2 deletions(-) diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 237b09010..7728f35df 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -13,8 +13,6 @@ Gem::Specification.new do |s| s.summary = "rspec-mocks-#{RSpec::Mocks::Version::STRING}" s.description = "RSpec's 'test double' framework, with support for stubbing and mocking" - s.rubyforge_project = "rspec" - s.files = `git ls-files -- lib/*`.split("\n") s.files += %w[README.md License.txt Changelog.md .yardopts .document] s.test_files = [] From af013b424cbff4beb13a1fc2d87c781fe6f9fa1d Mon Sep 17 00:00:00 2001 From: Mauricio Linhares Date: Sat, 30 May 2015 18:50:28 -0300 Subject: [PATCH 0907/1179] Show the right message when using spies and calling many methods This fixes the issue where when you called a method expected using `have_received` with an unexpected argument and called some other bogus method the spec error message wouldn't say the expected method was called with wrong arguments. Fixes #949. --- lib/rspec/mocks/proxy.rb | 13 ++++++++----- spec/rspec/mocks/matchers/have_received_spec.rb | 10 ++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index cc29b2102..9eecac5fc 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -101,12 +101,15 @@ def replay_received_message_on(expectation, &block) def check_for_unexpected_arguments(expectation) return if @messages_received.empty? - # Check if any of the arguments were expected. - return if @messages_received.any? do |(method_name, args, _)| - !expectation.matches_name_but_not_args(method_name, *args) - end + return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) } - raise_unexpected_message_args_error(expectation, messages_arg_list) + name_but_not_args, others = @messages_received.partition do |(method_name, args, _)| + expectation.matches_name_but_not_args(method_name, *args) + end + + return if name_but_not_args.empty? && !others.empty? + + raise_unexpected_message_args_error(expectation, name_but_not_args.map { |args| args[1] }) end # @private diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 93202a88a..3a1324af7 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -137,6 +137,16 @@ module Mocks expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' end + it 'produces an error message that matches the expected method if another method was called' do + my_spy = spy + my_spy.foo(1) + my_spy.bar(3) + + expect { + expect(my_spy).to have_received(:foo).with(3) + }.to fail_including("received :foo with unexpected arguments", "expected: (3)", "got: (1)") + end + context "counts" do let(:the_dbl) { double(:expected_method => nil) } From 9bd0a88dd5a1954cbb016285eae1a6e875609ddd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 30 May 2015 23:07:55 -0700 Subject: [PATCH 0908/1179] Add changelog for #960. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 545f91012..caa71ae7d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -41,6 +41,8 @@ Bug Fixes: which previously hit a `NoMethodError` internally in RSpec. (Myron Marston #954) * Fix edge case where the message received count would be incremented multiple times for one failure. (Myron Marston, #957) +* Fix failure messages for when spies received the expected message with + different arguments and also received another message. (Maurício Linhares, #960) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) From be0e2bfe125c2d0b11d8c8b954b84cb04faead21 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 30 May 2015 15:39:15 -0700 Subject: [PATCH 0909/1179] Add a spec for previously unspecified behavior. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will ensure I don’t break this behavior during a refactoring I’ll be doing next. --- .../mocks/stubbed_message_expectations_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index 0d0b89236..3d8c2f83e 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -23,6 +23,22 @@ reset dbl end + it 'indicates the site of expecation in the stacktrace when outputing arguments of similar calls' do + dbl = double('double', :foo => true) + expect(dbl).to receive(:foo).with('first'); line = __LINE__ + + dbl.foo('second') + dbl.foo('third') + + expect { + verify dbl + }.to raise_error(an_object_having_attributes( + :backtrace => a_collection_starting_with( + a_string_including("#{__FILE__}:#{line}") + ) + )) + end + context "with argument constraint on stub" do it "matches any args if no arg constraint set on expectation" do dbl = double("mock") From 4736f7a9c5378734dde4661b41e2275dacc9fe36 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 29 May 2015 16:38:33 -0700 Subject: [PATCH 0910/1179] Refactor: simplify how we insert expectation site on backtrace. --- lib/rspec/mocks/error_generator.rb | 28 +++++++++++++++---- lib/rspec/mocks/message_expectation.rb | 35 +++++++++--------------- spec/rspec/mocks/error_generator_spec.rb | 20 ++++++++++++++ 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 4cea8c0a6..6cea857e0 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -64,8 +64,8 @@ def raise_missing_default_stub_error(expectation, args_for_multiple_calls) end # @private - def raise_similar_message_args_error(expectation, args_for_multiple_calls) - __raise error_message(expectation, args_for_multiple_calls) + def raise_similar_message_args_error(expectation, args_for_multiple_calls, backtrace_line=nil) + __raise error_message(expectation, args_for_multiple_calls), backtrace_line end def default_error_message(expectation, expected_args, actual_args) @@ -79,10 +79,12 @@ def default_error_message(expectation, expected_args, actual_args) # rubocop:disable Style/ParameterLists # @private - def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, args) + def raise_expectation_error(message, expected_received_count, argument_list_matcher, + actual_received_count, expectation_count_type, args, + backtrace_line=nil) expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) received_part = received_part_of_expectation_error(actual_received_count, args) - __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}" + __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line end # rubocop:enable Style/ParameterLists @@ -295,9 +297,23 @@ def intro(unwrapped=false) end end - def __raise(message) + def __raise(message, backtrace_line=nil) message = opts[:message] unless opts[:message].nil? - notify RSpec::Mocks::MockExpectationError.new(message) + exception = RSpec::Mocks::MockExpectationError.new(message) + prepend_to_backtrace(exception, backtrace_line) if backtrace_line + notify exception + end + + if RSpec::Support::Ruby.jruby? + def prepend_to_backtrace(exception, line) + raise exception + rescue RSpec::Mocks::MockExpectationError => with_backtrace + with_backtrace.backtrace.unshift(line) + end + else + def prepend_to_backtrace(exception, line) + exception.set_backtrace(caller.unshift line) + end end def notify(exception) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 4d3b3de44..8053f4017 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -26,11 +26,10 @@ def called_max_times? end def verify_messages_received - InsertOntoBacktrace.line(@backtrace_line) do - unless @received - @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, []) - end - end + return if @received + @error_generator.raise_expectation_error( + @message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, [], @backtrace_line + ) end def unadvise(_) @@ -434,7 +433,7 @@ def matches_name_but_not_args(message, *args) def verify_messages_received return if expected_messages_received? - InsertOntoBacktrace.line(@expected_from) { generate_error } + generate_error end def expected_messages_received? @@ -476,9 +475,15 @@ def unadvise(args) def generate_error if similar_messages.empty? - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, expected_args) + @error_generator.raise_expectation_error( + @message, @expected_received_count, @argument_list_matcher, + @actual_received_count, expectation_count_type, expected_args, + @expected_from + ) else - @error_generator.raise_similar_message_args_error(self, @similar_messages) + @error_generator.raise_similar_message_args_error( + self, @similar_messages, @expected_from + ) end end @@ -706,19 +711,5 @@ def cannot_modify_further_error "to call the original implementation, and cannot be modified further." end end - - # Insert original locations into stacktraces - # - # @private - class InsertOntoBacktrace - RAISE_METHOD = method(:raise) - - def self.line(location, &block) - RSpec::Support.with_failure_notifier(RAISE_METHOD, &block) - rescue RSpec::Mocks::MockExpectationError => error - error.backtrace.insert(0, location) - RSpec::Support.notify_failure(error) - end - end end end diff --git a/spec/rspec/mocks/error_generator_spec.rb b/spec/rspec/mocks/error_generator_spec.rb index 373291488..3312a9edc 100644 --- a/spec/rspec/mocks/error_generator_spec.rb +++ b/spec/rspec/mocks/error_generator_spec.rb @@ -3,6 +3,26 @@ module RSpec module Mocks RSpec.describe ErrorGenerator do + context "when inserting a backtrace line" do + def has_java_frames? + yield + rescue RSpec::Mocks::MockExpectationError => e + e.backtrace.grep(/\.java:/).any? + else + raise "got no exception" + end + + it "produces stacktraces that match how `raise` produces stacktraces (on JRuby `caller` and `raise` can differ about the presence of java frames)" do + raise_has_java_frames = has_java_frames? { raise RSpec::Mocks::MockExpectationError } + + eg_has_java_frames = has_java_frames? do + ErrorGenerator.new.send(:__raise, "message", "foo.rb:1") + end + + expect(raise_has_java_frames).to eq eg_has_java_frames + end + end + def unexpected_failure_message_for(object_description) /received unexpected message :bees with \(#{object_description}\)/ end From 7a2a2cbab1d57d98f722aa1a2659ccbac7b7695b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 31 May 2015 00:30:50 -0700 Subject: [PATCH 0911/1179] Support 2nd `options` argument for a failure notifier. As needed by rspec/rspec-support#210. --- lib/rspec/mocks/matchers/have_received.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 27b890fdc..127b35871 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -96,7 +96,7 @@ def count_constraint end def generate_failure_message - RSpec::Support.with_failure_notifier(Proc.new { |e| return e.message }) do + RSpec::Support.with_failure_notifier(Proc.new { |err, _opt| return err.message }) do mock_proxy.check_for_unexpected_arguments(@expectation) @expectation.generate_error end From c7e864c4c0474c17619010533829571fdc7bba79 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 31 May 2015 12:44:26 -0700 Subject: [PATCH 0912/1179] Standardize on `verify_all`. --- spec/rspec/mocks/reraising_eager_raises_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/reraising_eager_raises_spec.rb b/spec/rspec/mocks/reraising_eager_raises_spec.rb index dc106d570..f77f26af5 100644 --- a/spec/rspec/mocks/reraising_eager_raises_spec.rb +++ b/spec/rspec/mocks/reraising_eager_raises_spec.rb @@ -32,7 +32,7 @@ with_unfulfilled_double do |dbl| expect(dbl).to receive(:foo).with(1,2,3) expect { dbl.foo(1,2,4) }.to fail - expect { RSpec::Mocks.verify }.to fail_with(/expected: 1 time with arguments: \(1, 2, 3\)/) + expect { verify_all }.to fail_with(/expected: 1 time with arguments: \(1, 2, 3\)/) end end @@ -43,7 +43,7 @@ expect(dbl).to receive(:bar).ordered expect { dbl.bar }.to fail dbl.foo # satisfy the `foo` expectation so that only the bar one fails below - expect { RSpec::Mocks.verify }.to fail_with(/received :bar out of order/) + expect { verify_all }.to fail_with(/received :bar out of order/) end end end From e4ad770fda8dd46b95d58b674a8db35df574f7e9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 1 Jun 2015 10:14:10 -0700 Subject: [PATCH 0913/1179] Remove unused code. --- lib/rspec/mocks/message_expectation.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 8053f4017..30934213a 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -378,7 +378,6 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub # Initialized to nil so that we don't allocate an array for every # mock or stub. See also comment in `and_yield`. @args_to_yield = nil - @failed_fast = nil @eval_context = nil @yield_receiver_to_implementation_block = false @@ -531,7 +530,6 @@ def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, args.unshift(orig_object) if yield_receiver_to_implementation_block? if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count)) - @failed_fast = true # args are the args we actually received, @argument_list_matcher is the # list of args we were expecting @error_generator.raise_expectation_error( @@ -563,10 +561,6 @@ def raise_already_invoked_error_if_necessary(calling_customization) error_generator.raise_already_invoked_error(message, calling_customization) end - def failed_fast? - @failed_fast - end - def set_expected_received_count(relativity, n) @at_least = (relativity == :at_least) @at_most = (relativity == :at_most) From c3716a07e405de08c9e85cc93f9222f9178d5bed Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 1 Jun 2015 10:17:52 -0700 Subject: [PATCH 0914/1179] Refactor: move exception helper method to `MessageExpectation` where it belongs. This will make it easier to pass a source id with it. --- lib/rspec/mocks/message_expectation.rb | 4 ++++ lib/rspec/mocks/proxy.rb | 9 ++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 30934213a..518d49974 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -486,6 +486,10 @@ def generate_error end end + def raise_unexpected_message_args_error(args_for_multiple_calls) + @error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls) + end + def expectation_count_type return :at_least if @at_least return :at_most if @at_most diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 9eecac5fc..b4d4a3a4d 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -109,7 +109,7 @@ def check_for_unexpected_arguments(expectation) return if name_but_not_args.empty? && !others.empty? - raise_unexpected_message_args_error(expectation, name_but_not_args.map { |args| args[1] }) + expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] }) end # @private @@ -184,7 +184,7 @@ def message_received(message, *args, &block) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? if null_object? || !has_negative_expectation?(message) - raise_unexpected_message_args_error(expectation, [args]) + expectation.raise_unexpected_message_args_error([args]) end elsif (stub = find_almost_matching_stub(message, *args)) stub.advise(*args) @@ -201,11 +201,6 @@ def raise_unexpected_message_error(method_name, args) @error_generator.raise_unexpected_message_error method_name, args end - # @private - def raise_unexpected_message_args_error(expectation, args_for_multiple_calls) - @error_generator.raise_unexpected_message_args_error(expectation, args_for_multiple_calls) - end - # @private def raise_missing_default_stub_error(expectation, args_for_multiple_calls) @error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls) From 8fca30924b473f32d2b74243f1d223bd683ea524 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 31 May 2015 22:53:06 -0700 Subject: [PATCH 0915/1179] Pass a `source_id` when notifying failures so we can dedup. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #956. In the long term, we may want to pass a `source_id` for every failure, but for now it’s sufficient for just a couple. --- lib/rspec/mocks/error_generator.rb | 16 +- lib/rspec/mocks/message_expectation.rb | 10 +- spec/rspec/mocks/failure_notification_spec.rb | 18 +++ .../mocks/reraising_eager_raises_spec.rb | 151 +++++++++++++++--- 4 files changed, 163 insertions(+), 32 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 6cea857e0..51f09f6b8 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -51,8 +51,8 @@ def raise_unexpected_message_error(message, args) end # @private - def raise_unexpected_message_args_error(expectation, args_for_multiple_calls) - __raise error_message(expectation, args_for_multiple_calls) + def raise_unexpected_message_args_error(expectation, args_for_multiple_calls, source_id=nil) + __raise error_message(expectation, args_for_multiple_calls), nil, source_id end # @private @@ -81,10 +81,10 @@ def default_error_message(expectation, expected_args, actual_args) # @private def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, args, - backtrace_line=nil) + backtrace_line=nil, source_id=nil) expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) received_part = received_part_of_expectation_error(actual_received_count, args) - __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line + __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line, source_id end # rubocop:enable Style/ParameterLists @@ -297,11 +297,11 @@ def intro(unwrapped=false) end end - def __raise(message, backtrace_line=nil) + def __raise(message, backtrace_line=nil, source_id=nil) message = opts[:message] unless opts[:message].nil? exception = RSpec::Mocks::MockExpectationError.new(message) prepend_to_backtrace(exception, backtrace_line) if backtrace_line - notify exception + notify exception, :source_id => source_id end if RSpec::Support::Ruby.jruby? @@ -316,8 +316,8 @@ def prepend_to_backtrace(exception, line) end end - def notify(exception) - RSpec::Support.notify_failure(exception) + def notify(*args) + RSpec::Support.notify_failure(*args) end def format_args(args) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 518d49974..75d59ecbd 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -477,7 +477,7 @@ def generate_error @error_generator.raise_expectation_error( @message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, expected_args, - @expected_from + @expected_from, exception_source_id ) else @error_generator.raise_similar_message_args_error( @@ -487,7 +487,7 @@ def generate_error end def raise_unexpected_message_args_error(args_for_multiple_calls) - @error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls) + @error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls, exception_source_id) end def expectation_count_type @@ -530,6 +530,10 @@ def increase_actual_received_count! private + def exception_source_id + @exception_source_id ||= "#{self.class.name} #{__id__}" + end + def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) args.unshift(orig_object) if yield_receiver_to_implementation_block? @@ -540,7 +544,7 @@ def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, @message, @expected_received_count, @argument_list_matcher, @actual_received_count + increment, - expectation_count_type, args + expectation_count_type, args, nil, exception_source_id ) end diff --git a/spec/rspec/mocks/failure_notification_spec.rb b/spec/rspec/mocks/failure_notification_spec.rb index 5b8a215c2..fceb15b4b 100644 --- a/spec/rspec/mocks/failure_notification_spec.rb +++ b/spec/rspec/mocks/failure_notification_spec.rb @@ -54,5 +54,23 @@ def capture_errors(&block) expect(e.message).to include("expected: (2)", "got: (1)") end end + + specify "failing negative expectations are only notified once" do + expect { + aggregate_failures do + dbl = double + + expect(dbl).not_to receive(:foo) + expect(dbl).not_to receive(:bar) + + dbl.foo + dbl.bar + + verify_all + end + }.to raise_error(RSpec::Expectations::MultipleExpectationsNotMetError) do |e| + expect(e.failures.count).to eq(2) + end + end end end diff --git a/spec/rspec/mocks/reraising_eager_raises_spec.rb b/spec/rspec/mocks/reraising_eager_raises_spec.rb index f77f26af5..c69919d74 100644 --- a/spec/rspec/mocks/reraising_eager_raises_spec.rb +++ b/spec/rspec/mocks/reraising_eager_raises_spec.rb @@ -8,42 +8,151 @@ end end - it "reraises when a negative expectation receives a call" do - with_unfulfilled_double do |dbl| - expect(dbl).not_to receive(:foo) - expect { dbl.foo }.to fail - expect { verify_all }.to fail_with(/expected: 0 times with any arguments/) + context "when a negative expectation receives a call" do + it "reraises during verification" do + with_unfulfilled_double do |dbl| + expect(dbl).not_to receive(:foo) + expect { dbl.foo }.to fail + expect { verify_all }.to fail_with(/expected: 0 times with any arguments/) + end + end + + it 'notifies both exceptions using the same `:source_id` so `aggregate_failures` can de-dup' do + with_unfulfilled_double do |dbl| + expect(dbl).not_to receive(:foo) + expect { dbl.foo }.to notify_with_same_source_id_as_later_verification + end + end + + it 'notifies with a different `source_id` than that for the same double and a different message' do + with_unfulfilled_double do |dbl| + expect(dbl).not_to receive(:foo) + + expect { + dbl.foo # should trigger first source_id + reset(dbl) + + # Prepare a failing expectation for a different message + expect(dbl).not_to receive(:bar) + RSpec::Support.with_failure_notifier(Proc.new { }) { dbl.bar } + }.not_to notify_with_same_source_id_as_later_verification + end + end + + it 'notifies with a different `source_id` than a different double expecting that message' do + with_unfulfilled_double do |dbl_1| + with_unfulfilled_double do |dbl_2| + expect(dbl_1).not_to receive(:foo) + expect(dbl_2).not_to receive(:foo) + + expect { dbl_2.foo }.to fail + expect { dbl_1.foo; reset(dbl_1) }.not_to notify_with_same_source_id_as_later_verification + end + end end end - it "reraises when an expectation with a count is exceeded" do - with_unfulfilled_double do |dbl| + context "when an expectation with a count is exceeded" do + def prepare(dbl) expect(dbl).to receive(:foo).exactly(2).times dbl.foo dbl.foo + end - expect { dbl.foo }.to fail - expect { verify_all }.to fail_with(/expected: 2 times with any arguments/) + it "reraises during verification" do + with_unfulfilled_double do |dbl| + prepare dbl + + expect { dbl.foo }.to fail + expect { verify_all }.to fail_with(/expected: 2 times with any arguments/) + end + end + + it 'notifies both exceptions using the same `:source_id` so `aggregate_failures` can de-dup' do + with_unfulfilled_double do |dbl| + prepare dbl + expect { dbl.foo }.to notify_with_same_source_id_as_later_verification + end end end - it "reraises when an expectation is called with the wrong arguments" do - with_unfulfilled_double do |dbl| - expect(dbl).to receive(:foo).with(1,2,3) - expect { dbl.foo(1,2,4) }.to fail - expect { verify_all }.to fail_with(/expected: 1 time with arguments: \(1, 2, 3\)/) + context "when an expectation is called with the wrong arguments" do + it "reraises during verification" do + with_unfulfilled_double do |dbl| + expect(dbl).to receive(:foo).with(1, 2, 3) + expect { dbl.foo(1, 2, 4) }.to fail + expect { verify_all }.to fail_with(/expected: 1 time with arguments: \(1, 2, 3\)/) + end + end + + it 'notifies both exceptions using the same `:source_id` so `aggregate_failures` can de-dup' do + with_unfulfilled_double do |dbl| + expect(dbl).to receive(:foo).with(1, 2, 3) + expect { dbl.foo(1, 2, 4) }.to notify_with_same_source_id_as_later_verification + end end end - it "reraises when an expectation is called out of order", + context "when an expectation is called out of order", :pending => "Says bar was called 0 times when it was, see: http://git.io/pjTq" do - with_unfulfilled_double do |dbl| - expect(dbl).to receive(:foo).ordered - expect(dbl).to receive(:bar).ordered - expect { dbl.bar }.to fail - dbl.foo # satisfy the `foo` expectation so that only the bar one fails below - expect { verify_all }.to fail_with(/received :bar out of order/) + it "reraises during verification" do + with_unfulfilled_double do |dbl| + expect(dbl).to receive(:foo).ordered + expect(dbl).to receive(:bar).ordered + expect { dbl.bar }.to fail + dbl.foo # satisfy the `foo` expectation so that only the bar one fails below + expect { verify_all }.to fail_with(/received :bar out of order/) + end + end + end + + RSpec::Matchers.define :notify_with_same_source_id_as_later_verification do + attr_reader :block + + match do |block| + @block = block + block_source_id == verify_all_source_id && block_source_id + end + + match_when_negated do |block| + @block = block + block_source_id && verify_all_source_id && ( + block_source_id != verify_all_source_id + ) + end + + supports_block_expectations + + failure_message do + if block_source_id.nil? + "expected it to notify with a non-nil source id" + else + "expected `verify_all` to notify with source_id: #{block_source_id.inspect} but notified with source_id: #{verify_all_source_id.inspect}" + end + end + + failure_message_when_negated do + if block_source_id.nil? + "expected it to notify with a non-nil source id" + else + "expected `verify_all` to notify with a different source_id but got the same one: #{block_source_id.inspect} / #{verify_all_source_id.inspect}" + end + end + + def block_source_id + @block_source_id ||= capture_notified_source_id(&block) + end + + def verify_all_source_id + @verify_all_source_id ||= capture_notified_source_id { verify_all } + end + + def capture_notified_source_id(&block) + source_id = nil + notifier = Proc.new { |err, opt| source_id = opt.fetch(:source_id) } + RSpec::Support.with_failure_notifier(notifier, &block) + source_id end end end From 8c1962ce4a67ba8e252b7b19a385d0dfde16dd63 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 2 Jun 2015 23:50:25 -0700 Subject: [PATCH 0916/1179] Work around odd travis failures on 1.8.7. --- spec/rspec/mocks/diffing_spec.rb | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index 0bc5c8163..3571b819f 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -52,7 +52,7 @@ expect(d).to receive(:foo).with(expected_hash) expect { d.foo(:bad => :hash) - }.to fail_with("# received :foo with unexpected arguments\n expected: (#{expected_hash.inspect})\n got: (#{actual_hash.inspect})\nDiff:\n@@ -1,2 +1,2 @@\n-[#{expected_hash.inspect}]\n+[#{actual_hash.inspect}]\n") + }.to fail_with(/\A# received :foo with unexpected arguments\n expected: \(#{hash_regex_inspect expected_hash}\)\n got: \(#{hash_regex_inspect actual_hash}\)\nDiff:\n@@ \-1\,2 \+1\,2 @@\n\-\[#{hash_regex_inspect expected_hash}\]\n\+\[#{hash_regex_inspect actual_hash}\]\n\z/) end end @@ -61,7 +61,22 @@ expect(d).to receive(:foo).with(expected_hash) expect { d.foo(Object.new) - }.to fail_with(/-\[#{Regexp.escape(expected_hash.inspect)}\].*\+\[#\]/m) + }.to fail_with(/-\[#{hash_regex_inspect expected_hash}\].*\+\[#\]/m) + end + end + + if RUBY_VERSION.to_f < 1.9 + # Ruby 1.8 hashes are not ordered, but `#inspect` on a particular unchanged + # hash instance should return consistent output. However, on Travis that does + # not always seem to be true and we have no idea why. Somehow, the travis build + # has occasionally failed due to the output ordering varying between `inspect` + # calls to the same hash. This regex allows us to work around that. + def hash_regex_inspect(hash) + "\\{(#{hash.map { |key,value| "#{key.inspect}=>#{value.inspect}.*" }.join "|"}){#{hash.size}}\\}" + end + else + def hash_regex_inspect(hash) + Regexp.escape(hash.inspect) end end From 7fca6101097e67caf715b9931118286017d98548 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 2 Jun 2015 23:56:13 -0700 Subject: [PATCH 0917/1179] Stop using deprecated failure notifier interface. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s going to require 2 arguments. --- spec/rspec/mocks/failure_notification_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/failure_notification_spec.rb b/spec/rspec/mocks/failure_notification_spec.rb index fceb15b4b..3b5af7422 100644 --- a/spec/rspec/mocks/failure_notification_spec.rb +++ b/spec/rspec/mocks/failure_notification_spec.rb @@ -1,7 +1,7 @@ RSpec.describe "Failure notification" do def capture_errors(&block) errors = [] - RSpec::Support.with_failure_notifier(lambda { |e| errors << e }, &block) + RSpec::Support.with_failure_notifier(Proc.new { |e, _opts| errors << e }, &block) errors end From e1b343e6fbe9ff5063bf080cf962348a4cdd8865 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 9 Jun 2015 08:11:32 -0700 Subject: [PATCH 0918/1179] Silence whitespace-only diffs. --- Changelog.md | 1 + lib/rspec/mocks/error_generator.rb | 2 +- spec/rspec/mocks/diffing_spec.rb | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index caa71ae7d..3f83ccc27 100644 --- a/Changelog.md +++ b/Changelog.md @@ -43,6 +43,7 @@ Bug Fixes: times for one failure. (Myron Marston, #957) * Fix failure messages for when spies received the expected message with different arguments and also received another message. (Maurício Linhares, #960) +* Silence whitespace-only diffs. (Myron Marston, #969) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 51f09f6b8..c8dfa6172 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -253,7 +253,7 @@ def error_message(expectation, args_for_multiple_calls) if args_for_multiple_calls.one? diff = diff_message(expectation.expected_args, args_for_multiple_calls.first) - message << "\nDiff:#{diff}" unless diff.empty? + message << "\nDiff:#{diff}" unless diff.strip.empty? end message diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index 3571b819f..15be1f030 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -16,6 +16,18 @@ end end + it "does not print a diff when differ returns a string of only whitespace" do + differ = instance_double(RSpec::Support::Differ, :diff => " \n \t ") + allow(RSpec::Support::Differ).to receive_messages(:new => differ) + + with_unfulfilled_double do |d| + expect(d).to receive(:foo).with("some string\nline2") + expect { + d.foo("this other string") + }.to fail_with(a_string_excluding("Diff:")) + end + end + it "prints a diff of the strings for individual mismatched multi-line string arguments" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with("some string\nline2") From de9ff29b71293ec4a1f9b14bba9953707a749435 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 12 Jun 2015 08:02:16 -0700 Subject: [PATCH 0919/1179] Updates changelog for v3.3.0 [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3f83ccc27..8de3823b5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.3.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.1...master) +### 3.3.0 / 2015-06-12 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.1...v3.3.0) Enhancements: From 66b1b1869a5f69d20f5cdd0c22027e8e93fa2652 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 12 Jun 2015 08:04:05 -0700 Subject: [PATCH 0920/1179] Release 3.3.0 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 4d95c39ea..9a1492951 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.3.0.pre' + STRING = '3.3.0' end end end From f7837f522072a890b7be7ad8f71b66eca3f18de9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 12 Jun 2015 08:35:07 -0700 Subject: [PATCH 0921/1179] Bump version to 3.4.0.pre. --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 9a1492951..dfe47386b 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.3.0' + STRING = '3.4.0.pre' end end end From 7ef9cb1018b67959ad8c4eb424e8bd87261d7989 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 13 Jun 2015 00:34:26 -0700 Subject: [PATCH 0922/1179] Add failing spec for #972. --- spec/rspec/mocks/partial_double_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index b96932e2c..63c924336 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -383,6 +383,22 @@ def defined_private_method object.implemented end + context "for a class" do + it "only runs the `before_verifying_doubles` callback for the class (not for superclasses)" do + subclass = Class.new(klass) + + expect { |probe| + RSpec.configuration.mock_with(:rspec) do |config| + config.before_verifying_doubles(&probe) + end + + allow(subclass).to receive(:new) + }.to yield_successive_args( + an_object_having_attributes(:target => subclass), + ) + end + end + it 'does not allow a non-existing method to be expected' do prevents { expect(object).to receive(:unimplemented) } end From 3555a4463b48f6fdf67825cc66fb67ce15502a31 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 14 Jun 2015 10:13:23 +0100 Subject: [PATCH 0923/1179] Only invoke verification callbacks on non-subclass proxies. Fixes #972 --- lib/rspec/mocks/proxy.rb | 2 +- lib/rspec/mocks/space.rb | 35 +++++++++++++++++++++---- lib/rspec/mocks/verifying_proxy.rb | 24 ++++++++++++++--- spec/rspec/mocks/partial_double_spec.rb | 2 +- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index b4d4a3a4d..97f0f680f 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -391,7 +391,7 @@ def superclass_proxy return @superclass_proxy if defined?(@superclass_proxy) if (superclass = object.superclass) - @superclass_proxy = @source_space.proxy_for(superclass) + @superclass_proxy = @source_space.superclass_proxy_for(superclass) else @superclass_proxy = nil end diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index 2ed35ebc3..dc91bba37 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -35,6 +35,10 @@ def registered?(_object) false end + def superclass_proxy_for(*_args) + raise_lifecycle_message + end + def new_scope Space.new end @@ -109,6 +113,13 @@ def proxy_for(object) end end + def superclass_proxy_for(klass) + proxy_mutex.synchronize do + id = id_for(klass) + proxies.fetch(id) { superclass_proxy_not_found_for(id, klass) } + end + end + alias ensure_registered proxy_for def registered?(object) @@ -150,11 +161,7 @@ def proxy_not_found_for(id, object) when NilClass then ProxyForNil.new(@expectation_ordering) when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering) when Class - if RSpec::Mocks.configuration.verify_partial_doubles? - VerifyingPartialClassDoubleProxy.new(self, object, @expectation_ordering) - else - PartialClassDoubleProxy.new(self, object, @expectation_ordering) - end + class_proxy_with_callback_verification_strategy(object, CallbackInvocationStrategy.new) else if RSpec::Mocks.configuration.verify_partial_doubles? VerifyingPartialDoubleProxy.new(object, @expectation_ordering) @@ -164,6 +171,24 @@ def proxy_not_found_for(id, object) end end + def superclass_proxy_not_found_for(id, object) + raise "superclass_proxy_not_found_for called with something that is not a class" unless Class === object + proxies[id] = class_proxy_with_callback_verification_strategy(object, NoCallbackInvocationStrategy.new) + end + + def class_proxy_with_callback_verification_strategy(object, strategy) + if RSpec::Mocks.configuration.verify_partial_doubles? + VerifyingPartialClassDoubleProxy.new( + self, + object, + @expectation_ordering, + strategy + ) + else + PartialClassDoubleProxy.new(self, object, @expectation_ordering) + end + end + def any_instance_recorder_not_found_for(id, klass) any_instance_recorders[id] = AnyInstance::Recorder.new(klass) end diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 5bb331929..198b099c2 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -3,6 +3,21 @@ module RSpec module Mocks + # @private + class CallbackInvocationStrategy + def call(doubled_module) + RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| + block.call doubled_module + end + end + end + + # @private + class NoCallbackInvocationStrategy + def call(_doubled_module) + end + end + # @private module VerifyingProxyMethods def add_stub(method_name, opts={}, &implementation) @@ -85,11 +100,14 @@ def validate_arguments!(method_name, args) end end + # @private + DEFAULT_CALLBACK_INVOCATION_STRATEGY = CallbackInvocationStrategy.new + # @private class VerifyingPartialDoubleProxy < PartialDoubleProxy include VerifyingProxyMethods - def initialize(object, expectation_ordering) + def initialize(object, expectation_ordering, optional_callback_invocation_strategy=DEFAULT_CALLBACK_INVOCATION_STRATEGY) super(object, expectation_ordering) @doubled_module = DirectObjectReference.new(object) @@ -99,9 +117,7 @@ def initialize(object, expectation_ordering) h[k] = VerifyingExistingMethodDouble.for(object, k, self) end - RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| - block.call @doubled_module - end + optional_callback_invocation_strategy.call(@doubled_module) end def method_reference diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 63c924336..39435dacf 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -394,7 +394,7 @@ def defined_private_method allow(subclass).to receive(:new) }.to yield_successive_args( - an_object_having_attributes(:target => subclass), + an_object_having_attributes(:target => subclass) ) end end From 0dd53d98281df53b51d4083d69e2fce20478e0b1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Jun 2015 18:54:56 -0700 Subject: [PATCH 0924/1179] Make `inspect` output deterministic to fix JRuby 1.8 mode failure. --- spec/rspec/mocks/diffing_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index 15be1f030..23c8d054e 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -140,6 +140,10 @@ def hash_regex_inspect(hash) def self.name "RSpec::Mocks::ArgumentMatchers::" end + + def inspect + "#" + end end.new expect(RSpec::Support.is_a_matcher?(collab)).to be true From 7c73ba09ac68fdaba327fc41817b8c43d0805dc9 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 19 Jun 2015 16:15:17 +0100 Subject: [PATCH 0925/1179] Add a changelog entry for #974 [ci skip] --- Changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Changelog.md b/Changelog.md index 8de3823b5..eeb8aec8b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 3.4.0 Development + +Bug Fixes: + +* Fix bug in before_verifying_double callback logic that caused it to be called + once for class in the ancestor list when mocking or stubbing a class. Now it + is only called for the mocked or stubbed class, as you would expect. (Sam + Phippen, #974) + ### 3.3.0 / 2015-06-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.1...v3.3.0) From 87eae6bfe8b8cabffc6005a2cfeff3c4050d8302 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 19 Jun 2015 16:44:59 +0100 Subject: [PATCH 0926/1179] Update changelog entry for #974 --- Changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index eeb8aec8b..6b9b16dc6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,9 +1,9 @@ -### 3.4.0 Development +### 3.3.1 Development Bug Fixes: -* Fix bug in before_verifying_double callback logic that caused it to be called - once for class in the ancestor list when mocking or stubbing a class. Now it +* Fix bug in `before_verifying_double` callback logic that caused it to be called + one for class in the ancestor list when mocking or stubbing a class. Now it is only called for the mocked or stubbed class, as you would expect. (Sam Phippen, #974) From dc33083904c4b31e7f2dfa3bf732baddce5dcb7a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Jun 2015 21:36:34 -0700 Subject: [PATCH 0927/1179] Forwardport 2.99.4 release notes. [ci skip] --- Changelog.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 6b9b16dc6..4971b1e1c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -365,7 +365,7 @@ Bug Fixes: behavior. (Maurício Linhares) ### 3.0.0.beta1 / 2013-11-07 -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.3...v3.0.0.beta1) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.4...v3.0.0.beta1) Breaking Changes for 3.0.0: @@ -428,6 +428,13 @@ Bug Fixes: returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) +### 2.99.4 / 2015-06-19 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.3...v2.99.4) + +Bug Fixes: + +* Add missing deprecation for using `with` with no arguments e.g. `with()`. (Yousuke, #970) + ### 2.99.3 / 2015-01-09 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.2...v2.99.3) From 68cfc3ea31343e876da9884abcfe4d583b47d28a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Jun 2015 21:42:11 -0700 Subject: [PATCH 0928/1179] Forward port 3.3.1 release notes. [ci skip] --- Changelog.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4971b1e1c..1f8a6103c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,14 @@ -### 3.3.1 Development +### 3.4.0 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.1...master) + +### 3.3.1 / 2015-06-19 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.0...v3.3.1) Bug Fixes: * Fix bug in `before_verifying_double` callback logic that caused it to be called - one for class in the ancestor list when mocking or stubbing a class. Now it - is only called for the mocked or stubbed class, as you would expect. (Sam + once for each class in the ancestor list when mocking or stubbing a class. Now + it is only called for the mocked or stubbed class, as you would expect. (Sam Phippen, #974) ### 3.3.0 / 2015-06-12 From 09fd6740e10a7923b6311acec792df7872e965e2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Jun 2015 22:12:28 -0700 Subject: [PATCH 0929/1179] Make `have_received` work without relying upon rspec-expectations. Fixes #967 and #968. --- Changelog.md | 5 + features/outside_rspec/minitest.feature | 36 +++- lib/rspec/mocks/matchers/have_received.rb | 22 +- lib/rspec/mocks/targets.rb | 4 +- .../mocks/matchers/have_received_spec.rb | 192 +++++++++++++----- spec/rspec/mocks/matchers/receive_spec.rb | 2 +- 6 files changed, 198 insertions(+), 63 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1f8a6103c..3603101a0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### 3.4.0 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.1...master) +Enhancements: + +* Make `expect(...).to have_received` work without relying upon + rspec-expectations. (Myron Marston, #978) + ### 3.3.1 / 2015-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.0...v3.3.1) diff --git a/features/outside_rspec/minitest.feature b/features/outside_rspec/minitest.feature index 0a5d9c856..b52c67047 100644 --- a/features/outside_rspec/minitest.feature +++ b/features/outside_rspec/minitest.feature @@ -61,6 +61,28 @@ Feature: Integrate with Minitest expect(dbl).to_not receive(:message) dbl.message end + + def test_passing_positive_spy_expectation + bond = spy + bond.james + expect(bond).to have_received(:james) + end + + def test_failing_positive_spy_expectation + bond = spy + expect(bond).to have_received(:james) + end + + def test_passing_negative_spy_expectation + bond = spy + expect(bond).not_to have_received(:james) + end + + def test_failing_negative_spy_expectation + bond = spy + bond.james + expect(bond).not_to have_received(:james) + end end """ When I run `ruby -Itest test/rspec_mocks_test.rb` @@ -77,4 +99,16 @@ Feature: Integrate with Minitest | expected: 1 time with any arguments | | received: 0 times with any arguments | | | - | 4 runs, 0 assertions, 0 failures, 2 errors, 0 skips | + | 3) Error: | + | RSpecMocksTest#test_failing_positive_spy_expectation: | + | RSpec::Mocks::MockExpectationError: (Double (anonymous)).james(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 4) Error: | + | RSpecMocksTest#test_failing_negative_spy_expectation: | + | RSpec::Mocks::MockExpectationError: (Double (anonymous)).james(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 8 runs, 0 assertions, 0 failures, 4 errors, 0 skips | diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 127b35871..ec3248c40 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -36,11 +36,11 @@ def does_not_match?(subject) end def failure_message - generate_failure_message + capture_failure_message end def failure_message_when_negated - generate_failure_message + capture_failure_message end def description @@ -54,6 +54,14 @@ def description end end + def setup_expectation(subject, &block) + notify_failure_message unless matches?(subject, &block) + end + + def setup_negative_expectation(subject, &block) + notify_failure_message unless does_not_match?(subject, &block) + end + def setup_allowance(_subject, &_block) disallow("allow", " as it would have no effect") end @@ -95,13 +103,17 @@ def count_constraint end end - def generate_failure_message + def capture_failure_message RSpec::Support.with_failure_notifier(Proc.new { |err, _opt| return err.message }) do - mock_proxy.check_for_unexpected_arguments(@expectation) - @expectation.generate_error + notify_failure_message end end + def notify_failure_message + mock_proxy.check_for_unexpected_arguments(@expectation) + @expectation.generate_error + end + def expected_messages_received_in_order? mock_proxy.replay_received_message_on @expectation, &@block @expectation.expected_messages_received? && @expectation.ensure_expected_ordering_received! diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 43436ccd3..5777810b9 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -19,7 +19,7 @@ def self.delegate_not_to(matcher_method, options={}) method_name = options.fetch(:from) define_method(method_name) do |matcher, &block| case matcher - when Matchers::Receive + when Matchers::Receive, Matchers::HaveReceived define_matcher(matcher, matcher_method, &block) when Matchers::ReceiveMessages, Matchers::ReceiveMessageChain raise_negation_unsupported(method_name, matcher) @@ -47,7 +47,7 @@ def define_matcher(matcher, name, &block) def raise_unsupported_matcher(method_name, matcher) raise UnsupportedMatcherError, - "only the `receive` or `receive_messages` matchers are supported " \ + "only the `receive`, `have_received` and `receive_messages` matchers are supported " \ "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 3a1324af7..3ec539635 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -1,6 +1,26 @@ module RSpec module Mocks - RSpec.describe Matchers::HaveReceived do + # This shared example group is highly unusual as it is used to test how + # `have_received` works in two situations: + # + # * With rspec-mocks as a standalone library. + # * Together with rspec-expectations. + # + # To simulate the former, we've had to hack things a bit. Special care must be taken: + # + # * Only define examples with `it`, (not `fit`, `xit`, `specify`, etc). We redefine + # `it` below to make it support our needs here but that definition isn't applied to + # the other forms. + # * All normal expectations must use `_expect`, not `expect`. Expectations + # for `have_received` should use `expect`. + RSpec.shared_examples_for Matchers::HaveReceived do + # Make rspec-expectations' `expect` available via an alias so that when + # this group is included below in a context that simulates rspec-expectations + # not being loaded by using rspec-mocks' `expect` instead of rspec-expectations' + # `expect`, we still have a way to use the expectations one for normal expectations. + # In this group, all normal expectations should use `_expect` instead of `expect`. + alias _expect expect + describe "expect(...).to have_received" do it 'passes when the double has received the given message' do dbl = double_with_met_expectation(:expected_method) @@ -15,15 +35,26 @@ module Mocks it 'fails when the double has not received the given message' do dbl = double_with_unmet_expectation(:expected_method) - expect { + _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/expected: 1 time/) end + it "notifies failures via rspec-support's failure notification system" do + dbl = double_with_unmet_expectation(:expected_method) + captured = nil + + RSpec::Support.with_failure_notifier(Proc.new { |e, _opt| captured = e }) do + expect(dbl).to have_received(:expected_method) + end + + _expect(captured.message).to include("expected: 1 time") + end + it 'fails when a null object has not received the given message' do dbl = double.as_null_object - expect { + _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/expected: 1 time/) end @@ -31,7 +62,7 @@ module Mocks it 'fails when the method has not been previously stubbed' do dbl = double - expect { + _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/method has not been stubbed/) end @@ -41,7 +72,7 @@ module Mocks expect(dbl).to receive(:expected_method) dbl.expected_method - expect { + _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/method has been mocked instead of stubbed/) end @@ -53,7 +84,7 @@ module Mocks expect(dbl).to have_received(:foo) { |*args| yielded << args } - expect(yielded).to include([:a,:b,:c]) + _expect(yielded).to include([:a,:b,:c]) end it "takes a do-end block and yields the arguments given to the stubbed method call" do @@ -63,15 +94,15 @@ module Mocks expect(dbl).to have_received(:foo) do |*args| yielded << args end - expect(yielded).to include([:a,:b,:c]) + _expect(yielded).to include([:a,:b,:c]) end it "passes if expectations against the yielded arguments pass" do dbl = double(:foo => nil) dbl.foo(42) - expect { + _expect { expect(dbl).to have_received(:foo) { |arg| - expect(arg).to eq(42) + _expect(arg).to eq(42) } }.to_not raise_error end @@ -79,9 +110,9 @@ module Mocks it "fails if expectations against the yielded arguments fail" do dbl = double(:foo => nil) dbl.foo(43) - expect { + _expect { expect(dbl).to have_received(:foo) { |arg| - expect(arg).to eq(42) + _expect(arg).to eq(42) } }.to raise_error(RSpec::Expectations::ExpectationNotMetError) end @@ -94,8 +125,8 @@ module Mocks expect(dbl).to have_received(:foo) { called << :curly } do called << :do_end end - expect(called).to include(:curly) - expect(called).not_to include(:do_end) + _expect(called).to include(:curly) + _expect(called).not_to include(:do_end) end it 'resets expectations on class methods when mocks are reset' do @@ -105,7 +136,7 @@ module Mocks reset dbl allow(dbl).to receive(:expected_method) - expect { + _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/0 times/) end @@ -119,7 +150,7 @@ module Mocks it 'fails when the given args do not match the args used with the message' do dbl = double_with_met_expectation(:expected_method, :expected, :args) - expect { + _expect { expect(dbl).to have_received(:expected_method).with(:unexpected, :args) }.to raise_error(/with unexpected arguments/) end @@ -127,14 +158,14 @@ module Mocks it 'generates a useful description' do matcher = have_received(:expected_method).with(:expected_args).once - expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' + _expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' end it 'can generate a description after mocks have been torn down (e.g. when rspec-core requests it)' do matcher = have_received(:expected_method).with(:expected_args).once matcher.matches?(double(:expected_method => 1)) RSpec::Mocks.teardown - expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' + _expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' end it 'produces an error message that matches the expected method if another method was called' do @@ -142,7 +173,7 @@ module Mocks my_spy.foo(1) my_spy.bar(3) - expect { + _expect { expect(my_spy).to have_received(:foo).with(3) }.to fail_including("received :foo with unexpected arguments", "expected: (3)", "got: (1)") end @@ -164,7 +195,7 @@ module Mocks context "when the message is received without any args matching" do it 'includes unmatched args in the error message' do - expect { + _expect { expect(the_dbl).to have_received(:expected_method).with(:three).once }.to fail_including("expected: (:three)", "got:", "(:one) (2 times)", @@ -174,7 +205,7 @@ module Mocks context "when the message is received too many times" do it 'includes the counts of calls with matching args in the error message' do - expect { + _expect { expect(the_dbl).to have_received(:expected_method).with(:one).once }.to fail_including("expected: 1 time", "received: 2 times") end @@ -182,7 +213,7 @@ module Mocks context "when the message is received too few times" do it 'includes the counts of calls with matching args in the error message' do - expect { + _expect { expect(the_dbl).to have_received(:expected_method).with(:two).twice }.to fail_including("expected: 2 times", "received: 1 time") end @@ -198,7 +229,7 @@ module Mocks dbl.expected_method(:three, :four) dbl.expected_method(:three, :four) - expect { + _expect { expect(dbl).to have_received(:expected_method).with(:four, :four).once }.to fail_including("expected: (:four, :four)", "got:","(:one, :four) (2 times)", @@ -210,7 +241,7 @@ module Mocks dbl = double(:expected_method => nil) dbl.expected_method(:one, :four) - expect { + _expect { expect(dbl).to have_received(:expected_method).with(:three, :four).once }.to fail_including("expected: (:three, :four)", "got: (:one, :four)") end @@ -220,7 +251,7 @@ module Mocks dbl.expected_method([:one], :four) dbl.expected_method(:one, [:four]) - expect { + _expect { expect(dbl).to have_received(:expected_method).with(:one, :four).once }.to fail_including("expected: (:one, :four)", "got:","([:one], :four)", @@ -231,7 +262,7 @@ module Mocks dbl = double(:expected_method => nil) dbl.expected_method(:one, :one, :two) - expect { + _expect { expect(dbl).to have_received(:expected_method).with(:one, :two, :three).once }.to fail_including("expected: (:one, :two, :three)", "got:","(:one, :one, :two)") end @@ -244,13 +275,13 @@ module Mocks end it 'fails when the message was received more times than expected' do - expect { + _expect { expect(the_dbl).to have_received(:expected_method).exactly(1).times }.to raise_error(/expected: 1 time.*received: 3 times/m) - expect { + _expect { expect(the_dbl).to have_received(:expected_method).exactly(2).times }.to raise_error(/expected: 2 times.*received: 3 times/m) - expect { + _expect { the_dbl.expected_method the_dbl.expected_method expect(the_dbl).to have_received(:expected_method).exactly(2).times @@ -258,7 +289,7 @@ module Mocks end it 'fails when the message was received fewer times' do - expect { + _expect { expect(the_dbl).to have_received(:expected_method).exactly(4).times }.to raise_error(/expected: 4 times.*received: 3 times/m) end @@ -274,7 +305,7 @@ module Mocks end it 'fails when the message was received fewer times' do - expect { + _expect { expect(the_dbl).to have_received(:expected_method).at_least(4).times }.to raise_error(/expected: at least 4 times.*received: 3 times/m) end @@ -290,7 +321,7 @@ module Mocks end it 'fails when the message was received more times' do - expect { + _expect { expect(the_dbl).to have_received(:expected_method).at_most(2).times }.to raise_error(/expected: at most 2 times.*received: 3 times/m) end @@ -306,7 +337,7 @@ module Mocks it 'fails when the message was never received' do dbl = double(:expected_method => nil) - expect { + _expect { expect(dbl).to have_received(:expected_method).once }.to raise_error(/expected: 1 time.*received: 0 times/m) end @@ -316,7 +347,7 @@ module Mocks dbl.expected_method dbl.expected_method - expect { + _expect { expect(dbl).to have_received(:expected_method).once }.to raise_error(/expected: 1 time.*received: 2 times/m) end @@ -335,7 +366,7 @@ module Mocks dbl = double(:expected_method => nil) dbl.expected_method - expect { + _expect { expect(dbl).to have_received(:expected_method).twice }.to raise_error(/expected: 2 times.*received: 1 time/m) end @@ -346,7 +377,7 @@ module Mocks dbl.expected_method dbl.expected_method - expect { + _expect { expect(dbl).to have_received(:expected_method).twice }.to raise_error(/expected: 2 times.*received: 3 times/m) end @@ -367,7 +398,7 @@ module Mocks dbl.expected_method dbl.expected_method - expect { + _expect { expect(dbl).to have_received(:expected_method).thrice }.to raise_error(/expected: 3 times.*received: 2 times/m) end @@ -379,7 +410,7 @@ module Mocks dbl.expected_method dbl.expected_method - expect { + _expect { expect(dbl).to have_received(:expected_method).thrice }.to raise_error(/expected: 3 times.*received: 4 times/m) end @@ -431,7 +462,7 @@ module Mocks the_dbl.two the_dbl.one - expect { + _expect { expect(the_dbl).to have_received(:one).twice.ordered expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) @@ -442,7 +473,7 @@ module Mocks the_dbl.two the_dbl.one - expect { + _expect { expect(the_dbl).to have_received(:one).at_most(2).times.ordered expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) @@ -453,7 +484,7 @@ module Mocks the_dbl.two the_dbl.one - expect { + _expect { expect(the_dbl).to have_received(:one).at_least(1).times.ordered expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) @@ -463,7 +494,7 @@ module Mocks the_dbl.two the_dbl.one - expect { + _expect { expect(the_dbl).to have_received(:one).ordered expect(the_dbl).to have_received(:two).ordered }.to raise_error(/received :two out of order/m) @@ -481,7 +512,7 @@ module Mocks end it "fails when the order is not matched" do - expect { + _expect { expect(the_dbl).to have_received(:one).with(2).ordered expect(the_dbl).to have_received(:one).with(1).ordered }.to fail_with(/received :one out of order/m) @@ -503,7 +534,7 @@ module Mocks end it 'fails when the messages are received out of order' do - expect { + _expect { expect(the_dbl).to have_received(:bar).ordered expect(the_dbl).to have_received(:foo).ordered }.to raise_error(/received :foo out of order/m) @@ -521,15 +552,26 @@ module Mocks it 'fails when the double has received the given message' do dbl = double_with_met_expectation(:expected_method) - expect { + _expect { expect(dbl).not_to have_received(:expected_method) }.to raise_error(/expected: 0 times.*received: 1 time/m) end + it "notifies failures via rspec-support's failure notification system" do + dbl = double_with_met_expectation(:expected_method) + captured = nil + + RSpec::Support.with_failure_notifier(Proc.new { |e, _opt| captured = e }) do + expect(dbl).not_to have_received(:expected_method) + end + + _expect(captured.message).to match(/expected: 0 times.*received: 1 time/m) + end + it 'fails when the method has not been previously stubbed' do dbl = double - expect { + _expect { expect(dbl).not_to have_received(:expected_method) }.to raise_error(/method has not been stubbed/) end @@ -543,7 +585,7 @@ module Mocks it 'fails when the given args match the args used with the message' do dbl = double_with_met_expectation(:expected_method, :expected, :args) - expect { + _expect { expect(dbl).not_to have_received(:expected_method).with(:expected, :args) }.to raise_error(/expected: 0 times.*received: 1 time/m) # TODO: better message end @@ -552,7 +594,7 @@ module Mocks %w(exactly at_least at_most times once twice).each do |constraint| it "does not allow #{constraint} to be used because it creates confusion" do dbl = double_with_unmet_expectation(:expected_method) - expect { + _expect { expect(dbl).not_to have_received(:expected_method).send(constraint) }.to raise_error(/can't use #{constraint} when negative/) end @@ -561,7 +603,7 @@ module Mocks describe "allow(...).to have_received" do it "fails because its nonsensical" do - expect { + _expect { allow(double).to have_received(:some_method) }.to fail_with("Using allow(...) with the `have_received` matcher is not supported as it would have no effect.") end @@ -569,7 +611,7 @@ module Mocks describe "allow_any_instance_of(...).to have_received" do it "fails because its nonsensical" do - expect { + _expect { allow_any_instance_of(double).to have_received(:some_method) }.to fail_with("Using allow_any_instance_of(...) with the `have_received` matcher is not supported.") end @@ -577,7 +619,7 @@ module Mocks describe "expect_any_instance_of(...).to have_received" do it "fails because we dont want to support it" do - expect { + _expect { expect_any_instance_of(double).to have_received(:some_method) }.to fail_with("Using expect_any_instance_of(...) with the `have_received` matcher is not supported.") end @@ -600,12 +642,54 @@ def meet_expectation(double, method_name, *args) def double_with_unmet_expectation(method_name) double('double', method_name => true) end + end - # Override `fail_including` for this context, since `have_received` is a normal - # rspec-expectations matcher, the error class is different. - def fail_including(*snippets) - raise_error(RSpec::Expectations::ExpectationNotMetError, a_string_including(*snippets)) + RSpec.describe Matchers::HaveReceived, "when used in a context that has rspec-mocks and rspec-expectations available" do + include_examples Matchers::HaveReceived do + # Override `fail_including` for this context, since `have_received` is a normal + # rspec-expectations matcher, the error class is different. + def fail_including(*snippets) + raise_error(RSpec::Expectations::ExpectationNotMetError, a_string_including(*snippets)) + end end end + + RSpec.describe Matchers::HaveReceived, "when used in a context that has only rspec-mocks available" do + # We use a delegator here so that matchers can still be created + # via the `RSpec::Matchers` methods. This works because we + # instantiate `MocksScope` with the example group instance, so + # all undefined methods (including matcher methods) forward to it. + # However, `RSpec::Mocks::ExampleMethods` defines `expect` so instances + # of this class use the version of `expect` defined in rspec-mocks, not + # the one from rspec-expectations. + class MocksScope + include RSpec::Mocks::ExampleMethods + + def initialize(example_group) + @example_group = example_group + end + + def method_missing(*args, &block) + @example_group.__send__(*args, &block) + end + end + + # Redefine `it` so that we can eval each example in a special scope + # to simulate rspec-expectations not being loaded. + def self.it(*args, &block) + # Necessary so line-number filtering works... + args << {} unless Hash === args.last + args.last[:caller] = caller + + # delegate to the normal `it`... + super(*args) do + # ...but eval the block in a special scope that has `expect` + # from rspec-mocks, not from rspec-expectations. + MocksScope.new(self).instance_exec(&block) + end + end + + include_examples Matchers::HaveReceived + end end end diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 2e3027ec4..1075c725f 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -491,7 +491,7 @@ def eq(value) framework.new.instance_exec do expect(3).to eq(3) end - }.to raise_error(/only the `receive` or `receive_messages` matchers are supported with `expect\(...\).to`/) + }.to raise_error(/only the `receive`, `have_received` and `receive_messages` matchers are supported with `expect\(...\).to`/) end it 'can toggle the available syntax' do From 008caa061a1bac7eff23ce53b4c8bd7b6be81ba0 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 23 Jun 2015 12:31:48 +0100 Subject: [PATCH 0930/1179] switch to using reentrant mutex to avoid deadlocks --- lib/rspec/mocks/space.rb | 14 +++----------- spec/rspec/mocks/partial_double_spec.rb | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/rspec/mocks/space.rb b/lib/rspec/mocks/space.rb index dc91bba37..f5a4c2406 100644 --- a/lib/rspec/mocks/space.rb +++ b/lib/rspec/mocks/space.rb @@ -1,3 +1,5 @@ +RSpec::Support.require_rspec_support 'reentrant_mutex' + module RSpec module Mocks # @private @@ -142,18 +144,8 @@ def any_instance_recorders_from_ancestry_of(object) private - # We don't want to depend on the stdlib ourselves, but if the user is - # using threads then a Mutex will be available to us. If not, we don't - # need to synchronize anyway. def new_mutex - defined?(::Mutex) ? ::Mutex.new : FakeMutex - end - - # @private - module FakeMutex - def self.synchronize - yield - end + Support::ReentrantMutex.new end def proxy_not_found_for(id, object) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 39435dacf..6bf979e94 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -383,6 +383,22 @@ def defined_private_method object.implemented end + it 'avoids deadlocks when a proxy is accessed from within a `before_verifying_doubles` callback' do + _klass = Class.new { def message; end; } + called_for = [] + + RSpec.configuration.mock_with(:rspec) do |config| + config.before_verifying_doubles do |ref| + unless called_for.include? ref.target + called_for << ref.target + ::RSpec::Mocks.space.proxy_for(ref.target) + end + end + end + + expect { allow(_klass.new).to receive(:message) }.to_not raise_error + end + context "for a class" do it "only runs the `before_verifying_doubles` callback for the class (not for superclasses)" do subclass = Class.new(klass) From 9477089da9d57dba323c7dc0dc85fa660c020d9b Mon Sep 17 00:00:00 2001 From: Bryce McDonnell Date: Fri, 3 Jul 2015 15:53:34 -0400 Subject: [PATCH 0931/1179] doc update for `receive_message_chain` and `receive` parity * summarizes conversation from rspec/rspec-mocks#921 * adds warning --- .../message_chains.feature | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/features/working_with_legacy_code/message_chains.feature b/features/working_with_legacy_code/message_chains.feature index 1627dc990..b685c35d5 100644 --- a/features/working_with_legacy_code/message_chains.feature +++ b/features/working_with_legacy_code/message_chains.feature @@ -1,6 +1,7 @@ Feature: Message Chains - You can use `receive_message_chain` in place of `receive` to stub a chain of messages: + You can use `receive_message_chain` in place of `receive` in certain instances + to stub a chain of messages: ```ruby allow(double).to receive_message_chain("foo.bar") { :baz } @@ -19,6 +20,12 @@ Feature: Message Chains ```ruby allow(Article).to receive_message_chain("recent.published") { [Article.new] } ``` + + `receive_message_chain` is designed to be used with evaluating a response like `and_return`, + `and_yield` etc. For legacy reasons, parity with `stub_chain` is supported but its uses are + not considered good practice. Support for `stub_chain` pairity may be removed in future versions. + + Common method chains like `with` and `exactly` (i.e. `exactly(2).times`) are not supported. Warning: ======== @@ -29,6 +36,14 @@ Feature: Message Chains `receive_message_chain` still results in brittle examples. For example, if you write `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the implementation calls `foo.baz.bar`, the stub will not work. + + Method chaining is not fully supported so `receive_message_chain` is not a perfect replacement + of `receive`. (see [Issue 921](https://github.com/rspec/rspec-mocks/issues/921) for a more detailed + explanation.) Chaining with `recieve_message_chain` creates ambiguity in how the chains should + be applied and applies design pressure on complex interactions in the implementation code. Other + mocking methods like `double` and `instance_double` provide a better way of testing code with these + interactions. + Scenario: Use `receive_message_chain` on a double Given a file named "receive_message_chain_spec.rb" with: From 141731eb78b80082147d0edad8049b509aedbd5f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 5 Jul 2015 00:25:18 -0700 Subject: [PATCH 0932/1179] Pin aruba to version that works on 1.8.7. Works around cucumber/aruba#279. --- rspec-mocks.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 7728f35df..94fe3b510 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -39,6 +39,6 @@ Gem::Specification.new do |s| s.add_development_dependency 'rake', '~> 10.0.0' s.add_development_dependency 'cucumber', '~> 1.3.15' - s.add_development_dependency 'aruba', '~> 0.5' + s.add_development_dependency 'aruba', '~> 0.6.2' # 0.7 is broken on ruby 1.8.7 s.add_development_dependency 'minitest', '~> 5.2' end From e6cf52138a8d5ad32f24a4e21a0a53b1b9ebe660 Mon Sep 17 00:00:00 2001 From: Liz Rush Date: Mon, 29 Jun 2015 15:09:28 -0700 Subject: [PATCH 0933/1179] Add means to fail test for expectations set on nil --- lib/rspec/mocks/example_methods.rb | 8 ++++++++ lib/rspec/mocks/proxy.rb | 11 +++++++++++ spec/rspec/mocks/nil_expectation_warning_spec.rb | 7 +++++++ 3 files changed, 26 insertions(+) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 4de71a2f6..080254c24 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -201,6 +201,14 @@ def allow_message_expectations_on_nil RSpec::Mocks.space.proxy_for(nil).warn_about_expectations = false end + # Raise an error when setting expectations on nil. + # + # By default warning messages are issued when expectations are set on + # nil. This is to force test failures when an expectation is set on nil. + def disallow_message_expectations_on_nil + RSpec::Mocks.space.proxy_for(nil).disallow_expectations = true + end + # Stubs the named constant with the given value. # Like method stubs, the constant will be restored # to its original value (or lack of one, if it was diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 97f0f680f..fb30ffaf3 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -407,23 +407,29 @@ class PartialClassDoubleProxy < PartialDoubleProxy class ProxyForNil < PartialDoubleProxy def initialize(order_group) @warn_about_expectations = true + @disallow_expectations = false super(nil, order_group) end attr_accessor :warn_about_expectations + attr_accessor :disallow_expectations alias warn_about_expectations? warn_about_expectations + alias disallow_expectations? disallow_expectations def add_message_expectation(method_name, opts={}, &block) + raise_error(method_name) if disallow_expectations? warn(method_name) if warn_about_expectations? super end def add_negative_message_expectation(location, method_name, &implementation) + raise_error(method_name) if disallow_expectations? warn(method_name) if warn_about_expectations? super end def add_stub(method_name, opts={}, &implementation) + raise_error(method_name) if disallow_expectations? warn(method_name) if warn_about_expectations? super end @@ -434,6 +440,11 @@ def warn(method_name) source = CallerFilter.first_non_rspec_line Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.") end + + def raise_error(method_name) + source = CallerFilter.first_non_rspec_line + raise("An expectation of :#{method_name} was set on nil. Called from #{source}. To allow expectations on nil, set disallow_expectations_on_nil to false.") + end end end end diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index 9e95e079a..e9869639e 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -24,6 +24,13 @@ module Mocks nil.foo end + it "raises an error when expectations on nil are disallowed" do + disallow_message_expectations_on_nil + + expect { expect(nil).to receive(:foo) }.to raise_error(StandardError) + expect { expect(nil).not_to receive(:bar) }.to raise_error(StandardError) + end + it 'does not call #nil? on a double extra times' do dbl = double expect(dbl).to receive(:nil?).once.and_return(false) From bcec550bfd91634cb6bc3c23117e5bd84b420a38 Mon Sep 17 00:00:00 2001 From: Bryce McDonnell Date: Wed, 8 Jul 2015 00:37:56 -0400 Subject: [PATCH 0934/1179] copy updates per PR review --- features/working_with_legacy_code/message_chains.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/working_with_legacy_code/message_chains.feature b/features/working_with_legacy_code/message_chains.feature index b685c35d5..2e39f4346 100644 --- a/features/working_with_legacy_code/message_chains.feature +++ b/features/working_with_legacy_code/message_chains.feature @@ -1,6 +1,6 @@ Feature: Message Chains - You can use `receive_message_chain` in place of `receive` in certain instances + You can use `receive_message_chain` in place of `receive` in certain circumstances to stub a chain of messages: ```ruby @@ -23,9 +23,9 @@ Feature: Message Chains `receive_message_chain` is designed to be used with evaluating a response like `and_return`, `and_yield` etc. For legacy reasons, parity with `stub_chain` is supported but its uses are - not considered good practice. Support for `stub_chain` pairity may be removed in future versions. + not considered good practice. Support for `stub_chain` parity may be removed in future versions. - Common method chains like `with` and `exactly` (i.e. `exactly(2).times`) are not supported. + Customisations like `exactly` (i.e. `exactly(2).times`) are not supported. Warning: ======== From 11ed907b26e5eaefb6f273d5913cb6f22d16ff5a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 8 Jul 2015 15:22:46 +1000 Subject: [PATCH 0935/1179] cleanup whitespace and reword --- .../message_chains.feature | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/features/working_with_legacy_code/message_chains.feature b/features/working_with_legacy_code/message_chains.feature index 2e39f4346..f7158e323 100644 --- a/features/working_with_legacy_code/message_chains.feature +++ b/features/working_with_legacy_code/message_chains.feature @@ -20,11 +20,11 @@ Feature: Message Chains ```ruby allow(Article).to receive_message_chain("recent.published") { [Article.new] } ``` - - `receive_message_chain` is designed to be used with evaluating a response like `and_return`, + + `receive_message_chain` is designed to be used with evaluating a response like `and_return`, `and_yield` etc. For legacy reasons, parity with `stub_chain` is supported but its uses are not considered good practice. Support for `stub_chain` parity may be removed in future versions. - + Customisations like `exactly` (i.e. `exactly(2).times`) are not supported. Warning: @@ -36,14 +36,14 @@ Feature: Message Chains `receive_message_chain` still results in brittle examples. For example, if you write `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the implementation calls `foo.baz.bar`, the stub will not work. - - Method chaining is not fully supported so `receive_message_chain` is not a perfect replacement - of `receive`. (see [Issue 921](https://github.com/rspec/rspec-mocks/issues/921) for a more detailed - explanation.) Chaining with `recieve_message_chain` creates ambiguity in how the chains should - be applied and applies design pressure on complex interactions in the implementation code. Other - mocking methods like `double` and `instance_double` provide a better way of testing code with these - interactions. - + + + Chaining with `recieve_message_chain` creates ambiguity in how the chains should + be applied and applies design pressure on complex interactions in the implementation + code. As such `receive_message_chain` is not a perfect replacement for `receive`. (see + [Issue 921](https://github.com/rspec/rspec-mocks/issues/921) for a more detailed + explanation). Other mocking methods like `double` and `instance_double` provide a + better way of testing code with these interactions. Scenario: Use `receive_message_chain` on a double Given a file named "receive_message_chain_spec.rb" with: From 584f710f0e3621988af58908039181a68af484f1 Mon Sep 17 00:00:00 2001 From: Liz Rush Date: Thu, 2 Jul 2015 17:10:53 -0700 Subject: [PATCH 0936/1179] Create config option for strict nil expectations --- lib/rspec/mocks/configuration.rb | 17 +++++++ lib/rspec/mocks/error_generator.rb | 10 ++++ lib/rspec/mocks/example_methods.rb | 9 +--- lib/rspec/mocks/proxy.rb | 48 +++++++++++++------ .../mocks/nil_expectation_warning_spec.rb | 34 +++++++++---- 5 files changed, 87 insertions(+), 31 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index e09a12a5c..f72e84dc6 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -3,12 +3,29 @@ module Mocks # Provides configuration options for rspec-mocks. class Configuration def initialize + @allow_message_expectations_on_nil = nil @yield_receiver_to_any_instance_implementation_blocks = true @verify_doubled_constant_names = false @transfer_nested_constants = false @verify_partial_doubles = false end + # Sets whether RSpec will warn, ignore, or fail a test when + # expectations are set on nil. + # By default, when this flag is not set, warning messages are issued when + # expectations are set on nil. This is to prevent false-positives and to + # catch potential bugs early on. + # When set to `true`, warning messages are suppressed. + # When set to `false`, it will raise an error. + # + # @example + # RSpec.configure do |config| + # config.mock_with :rspec do |mocks| + # mocks.allow_message_expectations_on_nil = false + # end + # end + attr_accessor :allow_message_expectations_on_nil + def yield_receiver_to_any_instance_implementation_blocks? @yield_receiver_to_any_instance_implementation_blocks end diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index c8dfa6172..a32960490 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -213,6 +213,16 @@ def raise_already_invoked_error(message, calling_customization) notify MockExpectationAlreadyInvokedError.new(error_message) end + def raise_expectation_on_nil_error(method_name) + __raise expectation_on_nil_message(method_name) + end + + def expectation_on_nil_message(method_name) + "An expectation of :#{method_name} was set on nil. " \ + "To allow expectations on nil & suppress this message, set allow_expectations_on_nil to true. " \ + "To disallow expectations on nil, set allow_expectations_on_nil to false" + end + private def received_part_of_expectation_error(actual_received_count, args) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 080254c24..e7c143d84 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -197,18 +197,11 @@ def class_spy(*args) # By default warning messages are issued when expectations are set on # nil. This is to prevent false-positives and to catch potential bugs # early on. + # @deprecated Use {RSpec::Mocks::Configuration#allow_message_expectations_on_nil} instead. def allow_message_expectations_on_nil RSpec::Mocks.space.proxy_for(nil).warn_about_expectations = false end - # Raise an error when setting expectations on nil. - # - # By default warning messages are issued when expectations are set on - # nil. This is to force test failures when an expectation is set on nil. - def disallow_message_expectations_on_nil - RSpec::Mocks.space.proxy_for(nil).disallow_expectations = true - end - # Stubs the named constant with the given value. # Like method stubs, the constant will be restored # to its original value (or lack of one, if it was diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index fb30ffaf3..286f91d75 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -406,44 +406,62 @@ class PartialClassDoubleProxy < PartialDoubleProxy # @private class ProxyForNil < PartialDoubleProxy def initialize(order_group) - @warn_about_expectations = true - @disallow_expectations = false + set_expectation_behavior super(nil, order_group) end - attr_accessor :warn_about_expectations attr_accessor :disallow_expectations - alias warn_about_expectations? warn_about_expectations - alias disallow_expectations? disallow_expectations + attr_accessor :warn_about_expectations def add_message_expectation(method_name, opts={}, &block) - raise_error(method_name) if disallow_expectations? - warn(method_name) if warn_about_expectations? + warn_or_raise!(method_name) super end def add_negative_message_expectation(location, method_name, &implementation) - raise_error(method_name) if disallow_expectations? - warn(method_name) if warn_about_expectations? + warn_or_raise!(method_name) super end def add_stub(method_name, opts={}, &implementation) - raise_error(method_name) if disallow_expectations? - warn(method_name) if warn_about_expectations? + warn_or_raise!(method_name) super end private + def set_expectation_behavior + case RSpec::Mocks.configuration.allow_message_expectations_on_nil + when false + @warn_about_expectations = false + @disallow_expectations = true + when true + @warn_about_expectations = false + @disallow_expectations = false + else + @warn_about_expectations = true + @disallow_expectations = false + end + end + + def warn_or_raise!(method_name) + # This method intentionally swallows the message when + # neither disallow_expectations nor warn_about_expectations + # are set to true. + if disallow_expectations + raise_error(method_name) + elsif warn_about_expectations + warn(method_name) + end + end + def warn(method_name) - source = CallerFilter.first_non_rspec_line - Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.") + warning_msg = @error_generator.expectation_on_nil_message(method_name) + RSpec.warning(warning_msg) end def raise_error(method_name) - source = CallerFilter.first_non_rspec_line - raise("An expectation of :#{method_name} was set on nil. Called from #{source}. To allow expectations on nil, set disallow_expectations_on_nil to false.") + @error_generator.raise_expectation_on_nil_error(method_name) end end end diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index e9869639e..acef2e1da 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -2,8 +2,10 @@ module RSpec module Mocks RSpec.describe "an expectation set on nil" do it "issues a warning with file and line number information" do - expected_warning = %r%An expectation of :foo was set on nil. Called from #{__FILE__}:#{__LINE__+3}(:in .+)?. Use allow_message_expectations_on_nil to disable warnings.% - expect(Kernel).to receive(:warn).with(expected_warning) + expected_warning = "WARNING: An expectation of :foo was set on nil. " \ + "To allow expectations on nil & suppress this message, set allow_expectations_on_nil to true. " \ + "To disallow expectations on nil, set allow_expectations_on_nil to false. Called from #{__FILE__}:#{__LINE__+3}(:in .+)?." + expect(Kernel).to receive(:warn).with(/#{expected_warning}/) expect(nil).to receive(:foo) nil.foo @@ -15,20 +17,36 @@ module Mocks expect(nil).not_to receive(:foo) end - it "does not issue a warning when expectations are set to be allowed" do + it 'does not issue a warning when expectations are set to be allowed' do allow_message_expectations_on_nil expect(Kernel).not_to receive(:warn) expect(nil).to receive(:foo) - expect(nil).not_to receive(:bar) + expect(nil).to_not receive(:bar) nil.foo end - it "raises an error when expectations on nil are disallowed" do - disallow_message_expectations_on_nil + context 'configured to allow expectation on nil' do + include_context 'with isolated configuration' - expect { expect(nil).to receive(:foo) }.to raise_error(StandardError) - expect { expect(nil).not_to receive(:bar) }.to raise_error(StandardError) + it 'does not issue a warning when expectations are set to be allowed' do + RSpec::Mocks.configuration.allow_message_expectations_on_nil = true + expect(Kernel).not_to receive(:warn) + + expect(nil).to receive(:foo) + expect(nil).not_to receive(:bar) + nil.foo + end + end + + context 'configured to disallow expectations on nil' do + include_context 'with isolated configuration' + + it "raises an error when expectations on nil are disallowed" do + RSpec::Mocks.configuration.allow_message_expectations_on_nil = false + expect { expect(nil).to receive(:foo) }.to raise_error(RSpec::Mocks::MockExpectationError) + expect { expect(nil).not_to receive(:bar) }.to raise_error(RSpec::Mocks::MockExpectationError) + end end it 'does not call #nil? on a double extra times' do From a5df57aa6feebec5317bae1ee7dd532c86d00582 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 10 Jul 2015 10:15:51 +1000 Subject: [PATCH 0937/1179] changelog for #983 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3603101a0..c92de6a6a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Enhancements: * Make `expect(...).to have_received` work without relying upon rspec-expectations. (Myron Marston, #978) +* Add option for failing tests when expectations are set on `nil`. + (Liz Rush, #983) ### 3.3.1 / 2015-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.0...v3.3.1) From 03956b5d24ff7f51b17de5d96d608bdeaed8e9f0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Jul 2015 21:08:31 -0700 Subject: [PATCH 0938/1179] Apply some slight improvements to nil expectation warning logic. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use backticks in the warning to indicate code snippets. - Make specs slightly less brittle by preferring the `output` matcher over mocking `Kernel.warn` (an interface we don’t own), and by specifying only a few important bits of the warning. --- lib/rspec/mocks/error_generator.rb | 6 +-- .../mocks/nil_expectation_warning_spec.rb | 46 ++++++++++++------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index a32960490..5035b5bed 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -218,9 +218,9 @@ def raise_expectation_on_nil_error(method_name) end def expectation_on_nil_message(method_name) - "An expectation of :#{method_name} was set on nil. " \ - "To allow expectations on nil & suppress this message, set allow_expectations_on_nil to true. " \ - "To disallow expectations on nil, set allow_expectations_on_nil to false" + "An expectation of `:#{method_name}` was set on `nil`. " \ + "To allow expectations on `nil` and suppress this message, set `config.allow_expectations_on_nil` to `true`. " \ + "To disallow expectations on `nil`, set `config.allow_expectations_on_nil` to `false`" end private diff --git a/spec/rspec/mocks/nil_expectation_warning_spec.rb b/spec/rspec/mocks/nil_expectation_warning_spec.rb index acef2e1da..8bb974e93 100644 --- a/spec/rspec/mocks/nil_expectation_warning_spec.rb +++ b/spec/rspec/mocks/nil_expectation_warning_spec.rb @@ -2,27 +2,33 @@ module RSpec module Mocks RSpec.describe "an expectation set on nil" do it "issues a warning with file and line number information" do - expected_warning = "WARNING: An expectation of :foo was set on nil. " \ - "To allow expectations on nil & suppress this message, set allow_expectations_on_nil to true. " \ - "To disallow expectations on nil, set allow_expectations_on_nil to false. Called from #{__FILE__}:#{__LINE__+3}(:in .+)?." - expect(Kernel).to receive(:warn).with(/#{expected_warning}/) + expect { + expect(nil).to receive(:foo) + }.to output(a_string_including( + "An expectation of `:foo` was set on `nil`", + "#{__FILE__}:#{__LINE__ - 3}" + )).to_stderr - expect(nil).to receive(:foo) nil.foo end it "issues a warning when the expectation is negative" do - expect(Kernel).to receive(:warn) - - expect(nil).not_to receive(:foo) + expect { + expect(nil).not_to receive(:foo) + }.to output(a_string_including( + "An expectation of `:foo` was set on `nil`", + "#{__FILE__}:#{__LINE__ - 3}" + )).to_stderr end it 'does not issue a warning when expectations are set to be allowed' do allow_message_expectations_on_nil - expect(Kernel).not_to receive(:warn) - expect(nil).to receive(:foo) - expect(nil).to_not receive(:bar) + expect { + expect(nil).to receive(:foo) + expect(nil).to_not receive(:bar) + }.not_to output.to_stderr + nil.foo end @@ -31,10 +37,12 @@ module Mocks it 'does not issue a warning when expectations are set to be allowed' do RSpec::Mocks.configuration.allow_message_expectations_on_nil = true - expect(Kernel).not_to receive(:warn) - expect(nil).to receive(:foo) - expect(nil).not_to receive(:bar) + expect { + expect(nil).to receive(:foo) + expect(nil).not_to receive(:bar) + }.not_to output.to_stderr + nil.foo end end @@ -63,8 +71,14 @@ module Mocks allow_message_expectations_on_nil RSpec::Mocks.teardown RSpec::Mocks.setup - expect(Kernel).to receive(:warn) - expect(nil).to receive(:foo) + + expect { + expect(nil).to receive(:foo) + }.to output(a_string_including( + "An expectation of `:foo` was set on `nil`", + "#{__FILE__}:#{__LINE__ - 3}" + )).to_stderr + nil.foo end From 3cf117c309b9acbdbda9442db6478a49345a4493 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Jul 2015 22:11:30 -0700 Subject: [PATCH 0939/1179] Ignore specs.out since our build scripts produce that as an artifact. [ci skip] --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5004e6d66..4d5c2b919 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ bundle .rbx .rspec-local spec/examples.txt +specs.out From e3396be15f15fd9032c372389b3b528faabcdecb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Jul 2015 22:24:52 -0700 Subject: [PATCH 0940/1179] Updated common markdown files (from rspec-dev) [ci skip] --- code_of_conduct.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 code_of_conduct.md diff --git a/code_of_conduct.md b/code_of_conduct.md new file mode 100644 index 000000000..9bd2d7ae6 --- /dev/null +++ b/code_of_conduct.md @@ -0,0 +1,42 @@ + + +# Contributor Code of Conduct + +For the purpose of building a welcoming, harassment-free community that +values contributions from anyone, the RSpec project has adopted the +following code of conduct. All contributors and participants (including +maintainers!) are expected to abide by its terms. + +As contributors and maintainers of this project, we pledge to respect all +people who contribute through reporting issues, posting feature requests, +updating documentation, submitting pull requests or patches, and other +activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion or similar personal characteristic. + +Examples of unacceptable behavior by participants include, but are not limited +to, the use of sexual language or imagery, derogatory comments or personal +attacks, trolling, public or private harassment, insults, or other +unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. Project maintainers who do not +follow the Code of Conduct may be removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by opening an issue or contacting one or more of the project +maintainers. + +This Code of Conduct is adapted from the [Contributor +Covenant](http://contributor-covenant.org), version 1.1.0, available at +[http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) From 8e7269a6c19fc851e919481ea52aa9a5aadd0124 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 10 Jul 2015 08:35:29 -0700 Subject: [PATCH 0941/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 26 +++++++++++++++++--------- script/predicate_functions.sh | 2 +- script/run_build | 3 ++- script/travis_functions.sh | 6 +++++- 8 files changed, 29 insertions(+), 16 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 53a1a087a..a06a8ce13 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. +# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index debb1a71a..9d1422e51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. +# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 8ddd66aa9..003c4ae9a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. +# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f7eb356e8..5a23af900 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. +# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index ef0284c38..3a9a827b9 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. +# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -67,13 +67,22 @@ function run_specs_one_by_one { function run_spec_suite_for { if [ ! -f ../$1/$SPECS_HAVE_RUN_FILE ]; then # don't rerun specs that have already run - echo "Running specs for $1" - pushd ../$1 - unset BUNDLE_GEMFILE - bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - travis_retry eval "bundle install $bundle_install_flags" - run_specs_and_record_done - popd + if [ -d ../$1 ]; then + echo "Running specs for $1" + pushd ../$1 + unset BUNDLE_GEMFILE + bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + travis_retry eval "bundle install $bundle_install_flags" + run_specs_and_record_done + popd + else + echo "" + echo "WARNING: The ../$1 directory does not exist. Usually the" + echo "travis build cds into that directory and run the specs to" + echo "ensure the specs still pass with your latest changes, but" + echo "we are going to skip that step." + echo "" + fi; fi; } @@ -119,7 +128,6 @@ function check_style_and_lint { } function run_all_spec_suites { - fold "one-by-one specs" run_specs_one_by_one fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index d9b9bb8ad..340d07deb 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. +# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 91e0b822b..4b8cc6d74 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. +# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -22,6 +22,7 @@ if style_and_lint_enforced; then fi if is_mri; then + fold "one-by-one specs" run_specs_one_by_one run_all_spec_suites else echo "Skipping the rest of the build on non-MRI rubies" diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 9832e7b7c..d7d8f38bd 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-05-05T17:56:26+10:00 from the rspec-dev repo. +# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: @@ -50,6 +50,8 @@ fold() { if [ -n "$TRAVIS" ]; then printf "travis_fold:start:%s\r\e[0m" "$name" travis_time_start + else + echo "============= Starting $name ===============" fi "$@" @@ -60,6 +62,8 @@ fold() { if [ "$status" -eq 0 ]; then if [ -n "$TRAVIS" ]; then printf "travis_fold:end:%s\r\e[0m" "$name" + else + echo "============= Ending $name ===============" fi else STATUS="$status" From 06972b2f872fab473fdb52118410083c52dd73a9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 15 Jul 2015 10:13:27 -0700 Subject: [PATCH 0942/1179] Forwardport 3.3.2 release notes. [ci skip] --- Changelog.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index c92de6a6a..3f1826c86 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.4.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.1...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.2...master) Enhancements: @@ -8,6 +8,14 @@ Enhancements: * Add option for failing tests when expectations are set on `nil`. (Liz Rush, #983) +### 3.3.2 / 2015-07-15 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.1...v3.3.2) + +Bug Fixes: + +* Prevent thread deadlock errors during proxy creation (e.g. when using + `before_verifying_doubles` callbacks). (Jon Rowe, #980, #979) + ### 3.3.1 / 2015-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.0...v3.3.1) From 13a846360252dee9edb2e3ce7a8da9372d1ecd7a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 20 Jul 2015 21:36:32 +1000 Subject: [PATCH 0943/1179] correct typo --- lib/rspec/mocks/syntax.rb | 2 +- spec/rspec/mocks/should_syntax_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/syntax.rb b/lib/rspec/mocks/syntax.rb index 1ba4dcd64..6305ab961 100644 --- a/lib/rspec/mocks/syntax.rb +++ b/lib/rspec/mocks/syntax.rb @@ -48,7 +48,7 @@ def stub(message_or_hash, opts={}, &block) end def unstub(message) - ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`") + ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to receive(...).and_call_original` or explicitly enable `:should`") ::RSpec::Mocks.space.proxy_for(self).remove_stub(message) end diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index 10243e161..6337b8d12 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -500,7 +500,7 @@ def use_rspec_mocks it "warns about unstubbing once, regardless of how many times it is called" do expect(RSpec).to receive(:deprecate).with(/Using.*without explicitly enabling/, - {:replacement => "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`"}) + {:replacement => "`allow(...).to receive(...).and_call_original` or explicitly enable `:should`"}) o = Object.new o2 = Object.new From 08f2c4823d60b2f1c87dcae255079f0db6a6c29e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 27 Jul 2015 23:49:06 -0700 Subject: [PATCH 0944/1179] Updated common markdown files (from rspec-dev) [ci skip] --- BUILD_DETAIL.md | 149 +++++++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 25 ++++++++ DEVELOPMENT.md | 132 +++++++++++++++++++++++++++++++++++++++ code_of_conduct.md | 2 +- 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 BUILD_DETAIL.md create mode 100644 CONTRIBUTING.md create mode 100644 DEVELOPMENT.md diff --git a/BUILD_DETAIL.md b/BUILD_DETAIL.md new file mode 100644 index 000000000..6a3bf7675 --- /dev/null +++ b/BUILD_DETAIL.md @@ -0,0 +1,149 @@ + + +# The CI build, in detail + +The [Travis CI build](https://travis-ci.org/rspec/rspec-mocks) +runs many verification steps to prevent regressions and +ensure high-quality code. To run the Travis build locally, run: + +``` +$ script/run_build +``` + +It can be useful to run the build steps individually +to repro a failing part of a Travis build. Let's break +the build down into the individual steps. + +## Specs + +RSpec dogfoods itself. Its primary defense against regressions is its spec suite. Run with: + +``` +$ bundle exec rspec + +# or, if you installed your bundle with `--standalone --binstubs`: + +$ bin/rspec +``` + +The spec suite performs a couple extra checks that are worth noting: + +* *That all the code is warning-free.* Any individual example that produces output + to `stderr` will fail. We also have a spec that loads all the `lib` and `spec` + files in a newly spawned process to detect load-time warnings and fail if there + are any. RSpec must be warning-free so that users who enable Ruby warnings will + not get warnings from our code. +* *That only a minimal set of stdlibs are loaded.* Since Ruby makes loaded libraries + available for use in any context, we want to minimize how many bits of the standard + library we load and use. Otherwise, RSpec's use of part of the standard library could + mask a problem where a gem author forgets to load a part of the standard library they + rely on. The spec suite contains a spec that defines a whitelist of allowed loaded + stdlibs. + +In addition, we use [SimpleCov](https://github.com/colszowka/simplecov) +to measure and enforce test coverage. If the coverage falls below a +project-specific threshold, the build will fail. + +## Cukes + +RSpec uses [cucumber](https://cucumber.io/) for both acceptance testing +and [documentation](https://relishapp.com/rspec). Since we publish our cukes +as documentation, please limit new cucumber scenarios to user-facing examples +that help demonstrate usage. Any tests that exist purely to prevent regressions +should be written as specs, even if they are written in an acceptance style. +Duplication between our YARD API docs and the cucumber documentation is fine. + +Run with: + +``` +$ bundle exec cucumber + +# or, if you installed your bundle with `--standalone --binstubs`: + +$ bin/cucumber +``` + +## YARD documentation + +RSpec uses [YARD](http://yardoc.org/) for API documentation on the [rspec.info site](http://rspec.info/). +Our commitment to [SemVer](htp://semver.org) requires that we explicitly +declare our public API, and our build uses YARD to ensure that every +class, module and method has either been labeled `@private` or has at +least some level of documentation. For new APIs, this forces us to make +an intentional decision about whether or not it should be part of +RSpec's public API or not. + +To run the YARD documentation coverage check, run: + +``` +$ bundle exec yard stats --list-undoc + +# or, if you installed your bundle with `--standalone --binstubs`: + +$ bin/yard stats --list-undoc +``` + +We also want to prevent YARD errors or warnings when actually generating +the docs. To check for those, run: + +``` +$ bundle exec yard doc --no-cache + +# or, if you installed your bundle with `--standalone --binstubs`: + +$ bin/yard doc --no-cache +``` + +## Rubocop + +We use [Rubocop](https://github.com/bbatsov/rubocop) to enforce style conventions on the project so +that the code has stylistic consistency throughout. Run with: + +``` +$ bundle exec rubocop lib + +# or, if you installed your bundle with `--standalone --binstubs`: + +$ bin/rubocop lib +``` + +Our Rubocop configuration is a work-in-progress, so if you get a failure +due to a Rubocop default, feel free to ask about changing the +configuration. Otherwise, you'll need to address the Rubocop failure, +or, as a measure of last resort, by wrapping the offending code in +comments like `# rubocop:disable SomeCheck` and `# rubocop:enable SomeCheck`. + +## Run spec files one-by-one + +A fast TDD cycle depends upon being able to run a single spec file, +without the rest of the test suite. While rare, it's fairly easy to +create a situation where a spec passes when the entire suite runs +but fails when its individual file is run. To guard against this, +our CI build runs each spec file individually, using a bit of bash like: + +``` +for file in `find spec -iname '*_spec.rb'`; do + echo "Running $file" + bin/rspec $file -b --format progress +done +``` + +Since this step boots RSpec so many times, it runs much, much +faster when we can avoid the overhead of bundler. This is a main reason our +CI build installs the bundle with `--standalone --binstubs` and +runs RSpec via `bin/rspec` rather than `bundle exec rspec`. + +## Running the spec suite for each of the other repos + +While each of the RSpec repos is an independent gem (generally designed +to be usable on its own), there are interdependencies between the gems, +and the specs for each tend to use features from the other gems. We +don't want to merge a pull request for one repo that might break the +build for another repo, so our CI build includes a spec that runs the +spec suite of each of the _other_ project repos. Note that we only run +the spec suite, not the full build, of the other projects, as the spec +suite runs very quickly compared to the full build. + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..fe9118a1d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,25 @@ + + +# Contributing + +RSpec is a community-driven project that has benefited from improvements from over over *500* contributors. +We welcome contributions from *everyone*. While contributing, please follow the project [code of conduct](code_of_conduct.md), so that everyone can be included. + +If you'd like to help make RSpec better, here are some ways you can contribute: + + - by running RSpec HEAD to help us catch bugs before new releases + - by [reporting bugs you encounter](https://github.com/rspec/rspec-mocks/issues/new) + - by [suggesting new features](https://github.com/rspec/rspec-mocks/issues/new) + - by improving RSpec's [Relish](https://relishapp.com/rspec) or [API](http://rspec.info/documentation/) documentation + - by improving [RSpec's website](http://rspec.info/) ([source](https://github.com/rspec/rspec.github.io)) + - by taking part in [feature and issue discussions](https://github.com/rspec/rspec-mocks/issues) + - by adding a failing test for reproducible [reported bugs](https://github.com/rspec/rspec-mocks/issues) + - by reviewing [pull requests](https://github.com/rspec/rspec-mocks/pulls) and suggesting improvements + - by [writing code](DEVELOPMENT.md) (no patch is too small! fix typos or bad whitespace) + +If you need help getting started, check out the [DEVELOPMENT](DEVELOPMENT.md) file for steps that will get you up and running. + +Thanks for helping us make RSpec better! diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 000000000..9908ab231 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,132 @@ + + +# Development Setup + +Generally speaking, you only need to clone the project and install +the dependencies with [Bundler](http://bundler.io/). You can either +get a full RSpec development environment using +[rspec-dev](https://github.com/rspec/rspec-dev#README) or you can +set this project up individually. + +## Setting up rspec-mocks individually + +For most contributors, setting up the project individually will be simpler. +Unless you have a specific reason to use rspec-dev, we recommend using this approach. + +Clone the repo: + +``` +$ git clone git@github.com:rspec/rspec-mocks.git +``` + +Install the dependencies using [Bundler](http://bundler.io/): + +``` +$ cd rspec-mocks +$ bundle install +``` + +To minimize boot time and to ensure we don't depend upon any extra dependencies +loaded by Bundler, our CI builds avoid loading Bundler at runtime +by using Bundler's [`--standalone option`](http://myronmars.to/n/dev-blog/2012/03/faster-test-boot-times-with-bundler-standalone). +While not strictly necessary (many/most of our contributors do not do this!), +if you want to exactly reproduce our CI builds you'll want to do the same: + +``` +$ bundle install --standalone --binstubs +``` + +The `--binstubs` option creates the `bin/rspec` file that, like `bundle exec rspec`, will load +all the versions specified in `Gemfile.lock` without loading bundler at runtime! + +## Using rspec-dev + +See the [rspec-dev README](https://github.com/rspec/rspec-dev#README) +for setup instructions. + +The rspec-dev project contains many rake tasks for helping manage +an RSpec development environment, making it easy to do things like: + +* Change branches across all repos +* Update all repos with the latest code from `master` +* Cut a new release across all repos +* Push out updated build scripts to all repos + +These sorts of tasks are essential for the RSpec maintainers but will +probably be unnecessary complexity if you're just contributing to one +repository. If you are getting setup to make your first contribution, +we recommend you take the simpler route of setting up rspec-mocks +individually. + +## Gotcha: Version mismatch from sibling repos + +The [Gemfile](Gemfile) is designed to be flexible and support using +the other RSpec repositories either from a local sibling directory +(e.g. `../rspec-`) or, if there is no such directory, +directly from git. This generally does the "right thing", but can +be a gotcha in some situations. For example, if you are setting up +`rspec-core`, and you happen to have an old clone of `rspec-expectations` +in a sibling directory, it'll be used even though it might be months or +years out of date, which can cause confusing failures. + +To avoid this problem, you can either `export USE_GIT_REPOS=1` to force +the use of `:git` dependencies instead of local dependencies, or update +the code in the sibling directory. rspec-dev contains rake tasks to +help you keep all repos in sync. + +## Extra Gems + +If you need additional gems for any tasks---such as `benchmark-ips` for benchmarking +or `byebug` for debugging---you can create a `Gemfile-custom` file containing those +gem declarations. The `Gemfile` evaluates that file if it exists, and it is git-ignored. + +# Running the build + +The [Travis CI build](https://travis-ci.org/rspec/rspec-mocks) +runs many verification steps to prevent regressions and +ensure high-quality code. To run the Travis build locally, run: + +``` +$ script/run_build +``` + +See [build detail](BUILD_DETAIL.md) for more detail. + +# What to Expect + +To ensure high, uniform code quality, all code changes (including +changes from the maintainers!) are subject to a pull request code +review. We'll often ask for clarification or suggest alternate ways +to do things. Our code reviews are intended to be a two-way +conversation. + +Here's a short, non-exhaustive checklist of things we typically ask contributors to do before PRs are ready to merge. It can help get your PR merged faster if you do these in advance! + +- [ ] New behavior is covered by tests and all tests are passing. +- [ ] No Ruby warnings are issued by your changes. +- [ ] Documentation reflects changes and renders as intended. +- [ ] Rubocop passes (e.g. `bundle exec rubocop lib`). +- [ ] Commits are squashed into a reasonable number of logical changesets that tell an easy-to-follow story. +- [ ] No changelog entry is necessary (we'll add it as part of the merge process!) + +# Adding Docs + +RSpec uses [YARD](http://yardoc.org/) for its API documentation. To +ensure the docs render well, we recommend running a YARD server and +viewing your edits in a browser. + +To run a YARD server: + +``` +$ bundle exec yard server --reload + +# or, if you installed your bundle with `--standalone --binstubs`: + +$ bin/yard server --reload +``` + +Then navigate to `localhost:8808` to view the rendered docs. + diff --git a/code_of_conduct.md b/code_of_conduct.md index 9bd2d7ae6..a761f5255 100644 --- a/code_of_conduct.md +++ b/code_of_conduct.md @@ -1,5 +1,5 @@ From 9be566d7f50707c430f8cbf763de2a13a326e9a6 Mon Sep 17 00:00:00 2001 From: Alex Egan Date: Fri, 31 Jul 2015 11:18:50 -0400 Subject: [PATCH 0945/1179] Add links to contributing details in README [ci skip] This commit adds links to the build details, code of conduct, contributing, and development setup markdown files to the README to easily access information about contributing to RSpec --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 701011723..c01395378 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,20 @@ RSpec repos as well. Add the following to your `Gemfile`: gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => 'master' end ``` +## Contributing + +Once you've set up the environment, you'll need to cd into the working +directory of whichever repo you want to work in. From there you can run the +specs and cucumber features, and make patches. + +NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You +can treat each RSpec repo as an independent project. + +For information about contributing to RSpec, please refer to the following markdown files: +* [Build details](BUILD_DETAIL.md) +* [Code of Conduct](code_of_conduct.md) +* [Detailed contributing guide](CONTRIBUTING.md) +* [Development setup guide](DEVELOPMENT.md) ## Test Doubles From 78647e71ae37532d41b62cd05badc83a12bb6fde Mon Sep 17 00:00:00 2001 From: Alex Egan Date: Fri, 31 Jul 2015 11:31:34 -0400 Subject: [PATCH 0946/1179] Rename code_of_conduct.md to CODE_OF_CONDUCT.md --- code_of_conduct.md => CODE_OF_CONDUCT.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename code_of_conduct.md => CODE_OF_CONDUCT.md (100%) diff --git a/code_of_conduct.md b/CODE_OF_CONDUCT.md similarity index 100% rename from code_of_conduct.md rename to CODE_OF_CONDUCT.md From de96b3aa9dc6a45374d885f7dd40d9761ac052db Mon Sep 17 00:00:00 2001 From: Alex Egan Date: Fri, 31 Jul 2015 11:33:11 -0400 Subject: [PATCH 0947/1179] Change link to code of conduct md file [ci skip] previous commit changed the name of code_of_conduct to CODE_OF_CONDUCT, so this fixes the link in the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c01395378..bae060509 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ can treat each RSpec repo as an independent project. For information about contributing to RSpec, please refer to the following markdown files: * [Build details](BUILD_DETAIL.md) -* [Code of Conduct](code_of_conduct.md) +* [Code of Conduct](CODE_OF_CONDUCT.md) * [Detailed contributing guide](CONTRIBUTING.md) * [Development setup guide](DEVELOPMENT.md) From 3b9bf81e58685888f64e31d0bf6feaa0e2208103 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Sat, 1 Aug 2015 02:04:28 -0400 Subject: [PATCH 0948/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 47 ++++++++++++++++++++++++++++++++++- script/predicate_functions.sh | 2 +- script/run_build | 4 ++- script/travis_functions.sh | 2 +- 8 files changed, 55 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index a06a8ce13..afe9af5e2 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. +# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 9d1422e51..4c230c0d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. +# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 003c4ae9a..386c6ebbb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. +# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 5a23af900..a3629ad65 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. +# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 3a9a827b9..ae7d0f34a 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. +# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -86,6 +86,51 @@ function run_spec_suite_for { fi; } +function check_binstubs { + echo "Checking required binstubs" + + local success=0 + local binstubs="" + local gems="" + + if [ ! -x ./bin/rspec ]; then + binstubs="$binstubs bin/rspec" + gems="$gems rspec-core" + success=1 + fi + + if [ ! -x ./bin/rake ]; then + binstubs="$binstubs bin/rake" + gems="$gems rake" + success=1 + fi + + if [ -d features ]; then + if [ ! -x ./bin/cucumber ]; then + binstubs="$binstubs bin/cucumber" + gems="$gems cucumber" + success=1 + fi + fi + + if [ $success -eq 1 ]; then + echo + echo "Missing binstubs:$binstubs" + echo "Install missing binstubs using one of the following:" + echo + echo " # Create the missing binstubs" + echo " $ bundle binstubs$gems" + echo + echo " # To binstub all gems" + echo " $ bundle install --binstubs" + echo + echo " # To binstub all gems and avoid loading bundler" + echo " $ bundle install --binstubs --standalone" + fi + + return $success +} + function check_documentation_coverage { echo "bin/yard stats --list-undoc" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 340d07deb..da3458090 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. +# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 4b8cc6d74..cb9a79b9f 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. +# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -10,6 +10,8 @@ if [ -f script/custom_build_functions.sh ]; then source script/custom_build_functions.sh fi +fold "binstub check" check_binstubs + fold "specs" run_specs_and_record_done fold "cukes" run_cukes diff --git a/script/travis_functions.sh b/script/travis_functions.sh index d7d8f38bd..cbbd04417 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-07-10T08:35:29-07:00 from the rspec-dev repo. +# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From adc7f32f9864487e1ad98837251b5482e4e28690 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 13 Aug 2015 10:40:53 +0100 Subject: [PATCH 0949/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 15 +++++++++++++-- script/predicate_functions.sh | 24 +++++++++++++++++++++++- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 42 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index afe9af5e2..f6c3fc9ba 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. +# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 4c230c0d5..7ab202ec6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. +# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 386c6ebbb..dc6e19cd7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. +# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index a3629ad65..a8e2fe4d9 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. +# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index ae7d0f34a..524631bd5 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. +# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -11,6 +11,12 @@ export JRUBY_OPTS=${JRUBY_OPTS:-"--server -Xcompile.invokedynamic=false"} SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` +# Don't allow rubygems to pollute what's loaded. Also, things boot faster +# without the extra load time of rubygems. Only works on MRI Ruby 1.9+ +if is_mri_192_plus; then + export RUBYOPT="--disable=gem" +fi + function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" @@ -46,10 +52,15 @@ function run_cukes { # the bin/cucumber approach below. That approach is faster # (as it avoids the bundler tax), so we use it on rubies where we can. bundle exec cucumber --strict + elif is_jruby; then + # For some reason JRuby doesn't like our improved bundler setup + RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ + PATH="${PWD}/bin:$PATH" \ + bin/cucumber --strict else # Prepare RUBYOPT for scenarios that are shelling out to ruby, # and PATH for those that are using `rspec` or `rake`. - RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ + RUBYOPT="${RUBYOPT} -I${PWD}/../bundle -rbundler/setup" \ PATH="${PWD}/bin:$PATH" \ bin/cucumber --strict fi diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index da3458090..aa75e0b7f 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. +# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -11,6 +11,16 @@ function is_mri { fi; } +function is_jruby { + if ruby -e "exit(defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java')"; then + # RUBY_ENGINE only returns 'ruby' on MRI. + # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) + return 0 + else + return 1 + fi; +} + function is_mri_192 { if is_mri; then if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then @@ -23,6 +33,18 @@ function is_mri_192 { fi } +function is_mri_192_plus { + if is_mri; then + if ruby -e "exit(RUBY_VERSION.to_f > 1.8)"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + function is_mri_2plus { if is_mri; then if ruby -e "exit(RUBY_VERSION.to_f > 2.0)"; then diff --git a/script/run_build b/script/run_build index cb9a79b9f..e458d6573 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. +# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index cbbd04417..de693f1a4 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-01T02:04:28-04:00 from the rspec-dev repo. +# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From bf55d4368cc62cfdd0294403d1b77409056da499 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 19 Aug 2015 07:44:10 -0700 Subject: [PATCH 0950/1179] Forward block received with message on to `have_received` block. Fixes #1003. --- Changelog.md | 5 +++++ lib/rspec/mocks/proxy.rb | 4 ++-- spec/rspec/mocks/matchers/have_received_spec.rb | 10 ++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3f1826c86..831de02d7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,11 @@ Enhancements: * Add option for failing tests when expectations are set on `nil`. (Liz Rush, #983) +Bug Fixes: + +* Fix `have_received { ... }` so that any block passed when the message + was received is forwarded to the `have_received` block. (Myron Marston, #1006) + ### 3.3.2 / 2015-07-15 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.1...v3.3.2) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 286f91d75..aa53e3066 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -89,11 +89,11 @@ def replay_received_message_on(expectation, &block) @error_generator.raise_expectation_on_unstubbed_method(expected_method_name) end - @messages_received.each do |(actual_method_name, args, _)| + @messages_received.each do |(actual_method_name, args, received_block)| next unless expectation.matches?(actual_method_name, *args) expectation.safe_invoke(nil) - block.call(*args) if block + block.call(*args, &received_block) if block end end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 3ec539635..f46b34fb6 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -129,6 +129,16 @@ module Mocks _expect(called).not_to include(:do_end) end + it 'forwards any block passed during method invocation to the `have_received` block' do + dbl = spy + block = lambda { } + dbl.foo(&block) + + expect(dbl).to have_received(:foo) do |&passed_block| + _expect(passed_block).to be block + end + end + it 'resets expectations on class methods when mocks are reset' do dbl = Object allow(dbl).to receive(:expected_method) From 104a565dd6aefb27a793e15f7121d4f62c2008a0 Mon Sep 17 00:00:00 2001 From: Pavel Pravosud Date: Thu, 20 Aug 2015 16:35:55 -0700 Subject: [PATCH 0951/1179] Fix typo: expecation -> expectation --- README.md | 2 +- lib/rspec/mocks/message_expectation.rb | 26 +++++++++---------- ...on.rb => verifying_message_expectation.rb} | 2 +- lib/rspec/mocks/verifying_proxy.rb | 2 +- spec/rspec/mocks/any_instance_spec.rb | 4 +-- spec/rspec/mocks/double_spec.rb | 4 +-- .../stubbed_message_expectations_spec.rb | 2 +- .../mocks/verifying_doubles/naming_spec.rb | 2 +- 8 files changed, 22 insertions(+), 22 deletions(-) rename lib/rspec/mocks/{verifying_message_expecation.rb => verifying_message_expectation.rb} (99%) diff --git a/README.md b/README.md index bae060509..64f675b98 100644 --- a/README.md +++ b/README.md @@ -360,7 +360,7 @@ end ## Delegating to the Original Implementation When working with a partial mock object, you may occasionally -want to set a message expecation without interfering with how +want to set a message expectation without interfering with how the object responds to the message. You can use `and_call_original` to achieve this: diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 75d59ecbd..76cc414ee 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -2,7 +2,7 @@ module RSpec module Mocks # A message expectation that only allows concrete return values to be set # for a message. While this same effect can be achieved using a standard - # MessageExpecation, this version is much faster and so can be used as an + # MessageExpectation, this version is much faster and so can be used as an # optimization. # # @private @@ -169,7 +169,7 @@ def and_throw(*args) # Tells the object to yield one or more args to a block when the message # is received. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # stream.stub(:open).and_yield(StringIO.new) def and_yield(*args, &block) @@ -191,7 +191,7 @@ def and_yield(*args, &block) # Constrain a message expectation to be received a specific number of # times. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(dealer).to receive(:deal_card).exactly(10).times def exactly(n, &block) @@ -204,7 +204,7 @@ def exactly(n, &block) # Constrain a message expectation to be received at least a specific # number of times. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(dealer).to receive(:deal_card).at_least(9).times def at_least(n, &block) @@ -223,7 +223,7 @@ def at_least(n, &block) # Constrain a message expectation to be received at most a specific # number of times. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(dealer).to receive(:deal_card).at_most(10).times def at_most(n, &block) @@ -235,7 +235,7 @@ def at_most(n, &block) # Syntactic sugar for `exactly`, `at_least` and `at_most` # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(dealer).to receive(:deal_card).exactly(10).times # expect(dealer).to receive(:deal_card).at_least(10).times @@ -247,7 +247,7 @@ def times(&block) # Expect a message not to be received at all. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(car).to receive(:stop).never def never @@ -258,7 +258,7 @@ def never # Expect a message to be received exactly one time. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(car).to receive(:go).once def once(&block) @@ -269,7 +269,7 @@ def once(&block) # Expect a message to be received exactly two times. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(car).to receive(:go).twice def twice(&block) @@ -280,7 +280,7 @@ def twice(&block) # Expect a message to be received exactly three times. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(car).to receive(:go).thrice def thrice(&block) @@ -302,7 +302,7 @@ def thrice(&block) # A message expectation will fail if the message is received with different # arguments. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # allow(cart).to receive(:add) { :failure } # allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } @@ -330,7 +330,7 @@ def with(*args, &block) # Expect messages to be received in a specific order. # - # @return [MessageExpecation] self, to support further chaining. + # @return [MessageExpectation] self, to support further chaining. # @example # expect(api).to receive(:prepare).ordered # expect(api).to receive(:run).ordered @@ -345,7 +345,7 @@ def ordered(&block) end # @private - # Contains the parts of `MessageExpecation` that aren't part of + # Contains the parts of `MessageExpectation` that aren't part of # rspec-mocks' public API. The class is very big and could really use # some collaborators it delegates to for this stuff but for now this was # the simplest way to split the public from private stuff to make it diff --git a/lib/rspec/mocks/verifying_message_expecation.rb b/lib/rspec/mocks/verifying_message_expectation.rb similarity index 99% rename from lib/rspec/mocks/verifying_message_expecation.rb rename to lib/rspec/mocks/verifying_message_expectation.rb index c81ef322b..c1faa5d3c 100644 --- a/lib/rspec/mocks/verifying_message_expecation.rb +++ b/lib/rspec/mocks/verifying_message_expectation.rb @@ -42,7 +42,7 @@ def validate_expected_arguments! verifier = Support::LooseSignatureVerifier.new(signature, args) unless verifier.valid? - # Fail fast is required, otherwise the message expecation will fail + # Fail fast is required, otherwise the message expectation will fail # as well ("expected method not called") and clobber this one. @failed_fast = true @error_generator.raise_invalid_arguments_error(verifier) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 198b099c2..a3e421722 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -1,4 +1,4 @@ -RSpec::Support.require_rspec_mocks 'verifying_message_expecation' +RSpec::Support.require_rspec_mocks 'verifying_message_expectation' RSpec::Support.require_rspec_mocks 'method_reference' module RSpec diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 6ef008b90..b53c805f2 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -918,13 +918,13 @@ def foo; end end context "when combined with other expectations" do - it "passes when the other expecations are met" do + it "passes when the other expectations are met" do expect_any_instance_of(klass).to receive(:foo).never expect_any_instance_of(klass).to receive(:existing_method).and_return(5) expect(klass.new.existing_method).to eq(5) end - it "fails when the other expecations are not met" do + it "fails when the other expectations are not met" do expect do expect_any_instance_of(klass).to receive(:foo).never expect_any_instance_of(klass).to receive(:existing_method).and_return(5) diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 422f4f859..86d049853 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -84,7 +84,7 @@ module Mocks } end - it "reports line number of expectation of unreceived message after a message expecation after similar stub" do + it "reports line number of expectation of unreceived message after a message expectation after similar stub" do allow(@double).to receive(:wont_happen) expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) expect { @@ -175,7 +175,7 @@ module Mocks end end - it 'does not get confused when a negative expecation is used with a string and symbol message' do + it 'does not get confused when a negative expectation is used with a string and symbol message' do allow(@double).to receive(:foo) { 3 } expect(@double).not_to receive(:foo).with(1) expect(@double).not_to receive("foo").with(2) diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index 3d8c2f83e..dac373a4e 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -23,7 +23,7 @@ reset dbl end - it 'indicates the site of expecation in the stacktrace when outputing arguments of similar calls' do + it 'indicates the site of expectation in the stacktrace when outputing arguments of similar calls' do dbl = double('double', :foo => true) expect(dbl).to receive(:foo).with('first'); line = __LINE__ diff --git a/spec/rspec/mocks/verifying_doubles/naming_spec.rb b/spec/rspec/mocks/verifying_doubles/naming_spec.rb index 392a91f68..0acd82fbc 100644 --- a/spec/rspec/mocks/verifying_doubles/naming_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/naming_spec.rb @@ -14,7 +14,7 @@ def error_message_for(verifying_double) end failure_message do |actual| - "expected #{actual.inspect} to fail expecations as:\n" + + "expected #{actual.inspect} to fail expectations as:\n" + " #{expected.inspect}, but failed with:\n" + " #{@error_message.inspect}" end From 7ab1cf71e2e83e5d7879092998141971e92bd72d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 21 Aug 2015 21:00:04 -0700 Subject: [PATCH 0952/1179] Docs: add missing nav item and link to configuring responses. --- features/.nav | 1 + features/configuring_responses/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/features/.nav b/features/.nav index 77f4a4ac3..4001274bd 100644 --- a/features/.nav +++ b/features/.nav @@ -18,6 +18,7 @@ - throwing.feature - yielding.feature - calling_the_original_implementation.feature + - wrapping_the_original_implementation.feature - block_implementation.feature - setting_constraints: - matching_arguments.feature diff --git a/features/configuring_responses/README.md b/features/configuring_responses/README.md index 25b731798..ccdf213b5 100644 --- a/features/configuring_responses/README.md +++ b/features/configuring_responses/README.md @@ -6,6 +6,7 @@ methods are provided to configure how the test double responds to the message. * `and_throw` * `and_yield` * `and_call_original` +* `and_wrap_original` In addition, you can provide a [block implementation](./configuring-responses/block-implementation) to respond in any manner you wish. From 5a21efd1eebfdd062c690e2c95440ddb3a0ac763 Mon Sep 17 00:00:00 2001 From: Brian John Date: Mon, 14 Sep 2015 20:38:40 -0500 Subject: [PATCH 0953/1179] fix link to the Code of Conduct from the Contributing guide --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe9118a1d..94cddf3b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ DO NOT modify it by hand as your changes will get lost the next time it is gener # Contributing RSpec is a community-driven project that has benefited from improvements from over over *500* contributors. -We welcome contributions from *everyone*. While contributing, please follow the project [code of conduct](code_of_conduct.md), so that everyone can be included. +We welcome contributions from *everyone*. While contributing, please follow the project [code of conduct](CODE_OF_CONDUCT.md), so that everyone can be included. If you'd like to help make RSpec better, here are some ways you can contribute: From 4edb7b9b9861c912aacfcdc1c9b4b59d523ae34d Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Wed, 30 Sep 2015 16:24:47 -0400 Subject: [PATCH 0954/1179] Sync up Readme footer with other repo --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 64f675b98..e4a795d93 100644 --- a/README.md +++ b/README.md @@ -439,6 +439,7 @@ you are interested in learning more, here is some recommended reading: ## Also see -* [http://github.com/rspec/rspec](http://github.com/rspec/rspec) -* [http://github.com/rspec/rspec-core](http://github.com/rspec/rspec-core) -* [http://github.com/rspec/rspec-expectations](http://github.com/rspec/rspec-expectations) +* [https://github.com/rspec/rspec](https://github.com/rspec/rspec) +* [https://github.com/rspec/rspec-core](https://github.com/rspec/rspec-core) +* [https://github.com/rspec/rspec-expectations](https://github.com/rspec/rspec-expectations) +* [https://github.com/rspec/rspec-rails](https://github.com/rspec/rspec-rails) From d92c0c0041f2cf36634ffd8f0a204479d1601c45 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 6 Oct 2015 16:48:03 +1100 Subject: [PATCH 0955/1179] Convert to markdown [skip ci] --- License.txt => License.md | 1 + 1 file changed, 1 insertion(+) rename License.txt => License.md (98%) diff --git a/License.txt b/License.md similarity index 98% rename from License.txt rename to License.md index 91cfc94f6..319d08b5d 100644 --- a/License.txt +++ b/License.md @@ -1,4 +1,5 @@ (The MIT License) +================= Copyright (c) 2012 David Chelimsky, Myron Marston Copyright (c) 2006 David Chelimsky, The RSpec Development Team From a2aadda61e46e918a006609a06958d0f5bfddd27 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 6 Oct 2015 16:57:31 +1100 Subject: [PATCH 0956/1179] Unify style [skip ci] --- License.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/License.md b/License.md index 319d08b5d..6912324e2 100644 --- a/License.md +++ b/License.md @@ -1,9 +1,9 @@ -(The MIT License) +The MIT License (MIT) ================= -Copyright (c) 2012 David Chelimsky, Myron Marston -Copyright (c) 2006 David Chelimsky, The RSpec Development Team -Copyright (c) 2005 Steven Baker +Copyright © 2012 David Chelimsky, Myron Marston +Copyright © 2006 David Chelimsky, The RSpec Development Team +Copyright © 2005 Steven Baker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From 54475922a43be1adc67fa9d3b4492e8e22a95e8b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 6 Oct 2015 20:25:40 +1100 Subject: [PATCH 0957/1179] fixup license file for build --- License.md => LICENSE.md | 4 ++-- rspec-mocks.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename License.md => LICENSE.md (90%) diff --git a/License.md b/LICENSE.md similarity index 90% rename from License.md rename to LICENSE.md index 6912324e2..cf95bb1dd 100644 --- a/License.md +++ b/LICENSE.md @@ -1,8 +1,8 @@ The MIT License (MIT) ================= -Copyright © 2012 David Chelimsky, Myron Marston -Copyright © 2006 David Chelimsky, The RSpec Development Team +Copyright © 2012 David Chelimsky, Myron Marston +Copyright © 2006 David Chelimsky, The RSpec Development Team Copyright © 2005 Steven Baker Permission is hereby granted, free of charge, to any person obtaining diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 94fe3b510..685418f5a 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.description = "RSpec's 'test double' framework, with support for stubbing and mocking" s.files = `git ls-files -- lib/*`.split("\n") - s.files += %w[README.md License.txt Changelog.md .yardopts .document] + s.files += %w[README.md LICENSE.md Changelog.md .yardopts .document] s.test_files = [] s.rdoc_options = ["--charset=UTF-8"] s.require_path = "lib" From 5428f957c59e45faf119a8036c86b713f6cea1ad Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 6 Oct 2015 21:49:12 +1100 Subject: [PATCH 0958/1179] list copyrights --- .yardopts | 1 - LICENSE.md | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.yardopts b/.yardopts index 15f63ee32..bf5950079 100644 --- a/.yardopts +++ b/.yardopts @@ -3,4 +3,3 @@ --markup markdown - Changelog.md -License.txt diff --git a/LICENSE.md b/LICENSE.md index cf95bb1dd..dae02d8a7 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,9 +1,9 @@ The MIT License (MIT) -================= +===================== -Copyright © 2012 David Chelimsky, Myron Marston -Copyright © 2006 David Chelimsky, The RSpec Development Team -Copyright © 2005 Steven Baker +* Copyright © 2012 David Chelimsky, Myron Marston +* Copyright © 2006 David Chelimsky, The RSpec Development Team +* Copyright © 2005 Steven Baker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From d6009064947af2cba7306bf71faa65689616214b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 7 Oct 2015 06:42:43 +1100 Subject: [PATCH 0959/1179] update yard --- .document | 2 +- .yardopts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.document b/.document index 050e20457..52a564f65 100644 --- a/.document +++ b/.document @@ -1,5 +1,5 @@ lib/**/*.rb - README.md -License.txt +LICENSE.md Changelog.md diff --git a/.yardopts b/.yardopts index bf5950079..9555b8e5c 100644 --- a/.yardopts +++ b/.yardopts @@ -3,3 +3,4 @@ --markup markdown - Changelog.md +LICENSE.md From dd86010af3e95881ad1a880e9f30a978c2638948 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 13 Oct 2015 18:52:08 -0700 Subject: [PATCH 0960/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 6 +++--- .travis.yml | 5 ++--- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index f6c3fc9ba..b2dd87abb 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. +# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects @@ -101,9 +101,9 @@ Proc: RedundantReturn: AllowMultipleReturnValues: true -# We have to rescue Exception in the `raise_error` matcher for it to work properly. +# Exceptions should be rescued with `Support::AllExceptionsExceptOnesWeMustNotRescue` RescueException: - Enabled: false + Enabled: true # We haven't adopted the `fail` to signal exceptions vs `raise` for re-raises convention. SignalException: diff --git a/.travis.yml b/.travis.yml index 7ab202ec6..b21b3185d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. +# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -8,9 +8,8 @@ cache: - ../bundle before_install: - "script/clone_all_rspec_repos" - # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it - - if [ -z "$JRUBY_OPTS" ]; then gem install bundler -v=1.5.3 && alias bundle="bundle _1.5.3_"; fi + - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler; fi bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: diff --git a/appveyor.yml b/appveyor.yml index dc6e19cd7..001e1bddd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. +# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index a8e2fe4d9..5ae735d27 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. +# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 524631bd5..ce3ffca3f 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. +# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index aa75e0b7f..a7d95d817 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. +# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index e458d6573..c508ef29f 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. +# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index de693f1a4..bc12598d0 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-08-13T10:40:53+01:00 from the rspec-dev repo. +# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 38549034f2865e93d0c2758c1bdb9135c2281ac0 Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Tue, 20 Oct 2015 19:20:25 +0200 Subject: [PATCH 0961/1179] Don't overflow stack if method expectation for #respond_to? fails In the process of generating the error message for a failed method expectation, ErrorGenerator would call Array#join on an array which contained the receiver. Internally, Array#join calls #respond_to?(:to_str) on the objects which are being joined into a string. If the method expectation was for #respond_to?, the internal call to #respond_to? could cause the expectation to fail again, causing an infinite loop and resulting stack overflow. Avoid this by not using Array#join to generate the error message. Fixes #1020. --- lib/rspec/mocks/error_generator.rb | 7 +------ spec/rspec/mocks/matchers/receive_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 5035b5bed..3ba04c6c0 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -69,12 +69,7 @@ def raise_similar_message_args_error(expectation, args_for_multiple_calls, backt end def default_error_message(expectation, expected_args, actual_args) - [ - intro, - "received", - expectation.message.inspect, - unexpected_arguments_message(expected_args, actual_args), - ].join(" ") + "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}" end # rubocop:disable Style/ParameterLists diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 1075c725f..653ca4caa 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -393,6 +393,27 @@ def receiver.method_missing(*a); end # a poor man's stub... reset_all end end + + context "when expected message is respond_to?" do + context "and conditions are placed on expected call" do + context "and the conditions fail... :(" do + it "does not result in infinite recursion and stack overflow" do + # Setting a method expectation causes the method to be proxied + # RSpec may call #respond_to? when processing a failed expectation + # If those internal calls go to the proxied method, that could + # result in another failed expectation error, causing infinite loop + + expect { + obj = Object.new + expect(obj).to receive(:respond_to?).with('something highly unlikely') + obj.respond_to?(:not_what_we_wanted) + }.to raise_error(/received :respond_to\? with unexpected arguments/) + + reset_all + end + end + end + end end describe "expect_any_instance_of(...).to receive" do From ae97efb0e430646cdcb0fa934b80c756f484abd9 Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Tue, 20 Oct 2015 19:25:58 +0200 Subject: [PATCH 0962/1179] Fix misspelling of 'vague' (was: 'vauge') --- spec/rspec/mocks/matchers/have_received_spec.rb | 8 ++++---- spec/rspec/mocks/matchers/receive_spec.rb | 4 ++-- spec/spec_helper.rb | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index f46b34fb6..cbeeff4fc 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -448,7 +448,7 @@ module Mocks expect(the_dbl).to have_received(:three).once.ordered end - it 'passes with at most receive counts when received in order', :ordered_and_vauge_counts_unsupported do + it 'passes with at most receive counts when received in order', :ordered_and_vague_counts_unsupported do the_dbl.one the_dbl.one the_dbl.two @@ -458,7 +458,7 @@ module Mocks expect(the_dbl).to have_received(:two).once.ordered end - it 'passes with at least receive counts when received in order', :ordered_and_vauge_counts_unsupported do + it 'passes with at least receive counts when received in order', :ordered_and_vague_counts_unsupported do the_dbl.one the_dbl.one the_dbl.two @@ -478,7 +478,7 @@ module Mocks }.to raise_error(/received :two out of order/m) end - it "fails with at most receive counts when recieved out of order", :ordered_and_vauge_counts_unsupported do + it "fails with at most receive counts when recieved out of order", :ordered_and_vague_counts_unsupported do the_dbl.one the_dbl.two the_dbl.one @@ -489,7 +489,7 @@ module Mocks }.to raise_error(/received :two out of order/m) end - it "fails with at least receive counts when recieved out of order", :ordered_and_vauge_counts_unsupported do + it "fails with at least receive counts when recieved out of order", :ordered_and_vague_counts_unsupported do the_dbl.one the_dbl.two the_dbl.one diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 653ca4caa..c333c80eb 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -359,7 +359,7 @@ def receiver.method_missing(*a); end # a poor man's stub... dbl.two end - it "fails with at least when the ordering is incorrect", :ordered_and_vauge_counts_unsupported do + it "fails with at least when the ordering is incorrect", :ordered_and_vague_counts_unsupported do expect { expect(dbl).to receive(:one).at_least(2).times.ordered expect(dbl).to receive(:two).once.ordered @@ -379,7 +379,7 @@ def receiver.method_missing(*a); end # a poor man's stub... dbl.two end - it "fails with at most when the ordering is incorrect", :ordered_and_vauge_counts_unsupported do + it "fails with at most when the ordering is incorrect", :ordered_and_vague_counts_unsupported do expect { expect(dbl).to receive(:one).at_most(2).times.ordered expect(dbl).to receive(:two).once.ordered diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2edc9f29b..0c563fb2d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -109,14 +109,14 @@ def self.fake_matcher_description config.extend RSpec::Support::RubyFeatures config.include RSpec::Support::RubyFeatures - config.define_derived_metadata :ordered_and_vauge_counts_unsupported do |meta| - meta[:pending] = "`.ordered` combined with a vauge count (e.g. `at_least` or `at_most`) is not yet supported (see #713)" + config.define_derived_metadata :ordered_and_vague_counts_unsupported do |meta| + meta[:pending] = "`.ordered` combined with a vague count (e.g. `at_least` or `at_most`) is not yet supported (see #713)" end # We have yet to try to address this issue, and it's just noise in our output, # so skip it locally. However, on CI we want it to still run them so that if # we do something that makes these specs pass, we are notified. - config.filter_run_excluding :ordered_and_vauge_counts_unsupported unless ENV['CI'] + config.filter_run_excluding :ordered_and_vague_counts_unsupported unless ENV['CI'] RSpec::Matchers.define_negated_matcher :a_string_excluding, :include end From 07ad814ce293f87f2b0553bad565b4e034390422 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 20 Oct 2015 21:05:54 +1100 Subject: [PATCH 0963/1179] supress warnings from not specifying error --- spec/rspec/mocks/matchers/receive_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 1075c725f..45cb5da90 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -366,7 +366,7 @@ def receiver.method_missing(*a); end # a poor man's stub... dbl.one dbl.two - }.to raise_error + }.to fail reset_all end @@ -388,7 +388,7 @@ def receiver.method_missing(*a); end # a poor man's stub... dbl.one dbl.one dbl.two - }.to raise_error + }.to fail reset_all end From 709125074b05541acb6762d1677c24c0a9912a7e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 21 Oct 2015 10:33:08 +1100 Subject: [PATCH 0964/1179] cleanup and changelog for #1022 --- Changelog.md | 2 ++ spec/rspec/mocks/matchers/receive_spec.rb | 30 +++++++++-------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/Changelog.md b/Changelog.md index 831de02d7..ba56f136e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,8 @@ Bug Fixes: * Fix `have_received { ... }` so that any block passed when the message was received is forwarded to the `have_received` block. (Myron Marston, #1006) +* Fix infinite loop in error generator when stubbing `respond_to?`. + (Alex Dowad, #1022) ### 3.3.2 / 2015-07-15 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.1...v3.3.2) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index c333c80eb..098a7725b 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -392,26 +392,18 @@ def receiver.method_missing(*a); end # a poor man's stub... reset_all end - end - - context "when expected message is respond_to?" do - context "and conditions are placed on expected call" do - context "and the conditions fail... :(" do - it "does not result in infinite recursion and stack overflow" do - # Setting a method expectation causes the method to be proxied - # RSpec may call #respond_to? when processing a failed expectation - # If those internal calls go to the proxied method, that could - # result in another failed expectation error, causing infinite loop - - expect { - obj = Object.new - expect(obj).to receive(:respond_to?).with('something highly unlikely') - obj.respond_to?(:not_what_we_wanted) - }.to raise_error(/received :respond_to\? with unexpected arguments/) - reset_all - end - end + it 'does not result in infinite recursion when `respond_to?` is stubbed' do + # Setting a method expectation causes the method to be proxied + # RSpec may call #respond_to? when processing a failed expectation + # If those internal calls go to the proxied method, that could + # result in another failed expectation error, causing infinite loop + expect { + obj = Object.new + expect(obj).to receive(:respond_to?).with('something highly unlikely') + obj.respond_to?(:not_what_we_wanted) + }.to raise_error(/received :respond_to\? with unexpected arguments/) + reset_all end end end From 5bd0b5aceca2f73483c9861e1c955c07c1a32903 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 22 Sep 2015 20:19:01 +1000 Subject: [PATCH 0965/1179] allow stubbing respond_to? on partial doubles --- lib/rspec/mocks/method_reference.rb | 19 ++++++++++++++++++- spec/rspec/mocks/partial_double_spec.rb | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 513706e37..70dcd796d 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -94,7 +94,24 @@ def self.method_visibility_for(object, method_name) # write `method_missing` in such a way that it handles a dynamic message # with private or protected visibility. Ruby doesn't provide you with # the caller info. - return :public if vis.nil? && object.respond_to?(method_name, false) + return :public if vis.nil? && object_responds_to?(object, method_name, false) + end + end + + # @api private + KERNEL_RESPOND_TO_METHOD = ::Kernel.instance_method(:respond_to?) + + if Support::RubyFeatures.supports_rebinding_module_methods? + def self.object_responds_to?(object, method_name, visibility) + KERNEL_RESPOND_TO_METHOD.bind(object).call(method_name, visibility) + end + else + def self.object_responds_to?(object, method_name, visibility) + if ::Kernel === object + KERNEL_RESPOND_TO_METHOD.bind(object).call(method_name, visibility) + else + object.respond_to?(method_name, visibility) + end end end end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 6bf979e94..643344ebd 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -43,6 +43,23 @@ module Mocks expect(object.bar).to eq(2) end + it 'allows `respond_to?` to be stubbed' do + the_klass = Class.new do + def call(name) + if respond_to?(name) + send(name) + end + end + end + + an_object = the_klass.new + + expect(an_object).to receive(:respond_to?).with(:my_method) { true } + expect(an_object).to receive(:my_method) + + an_object.call :my_method + end + it "can disallow messages from being received" do expect(object).not_to receive(:fuhbar) expect_fast_failure_from( From cb45259b21b24680c4be8025cb8480abbc3b9997 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 20 Oct 2015 20:01:43 +1100 Subject: [PATCH 0966/1179] use objects original respond_to if stubbed --- lib/rspec/mocks/method_reference.rb | 22 +++++++--------------- lib/rspec/mocks/proxy.rb | 5 +++++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 70dcd796d..0831abb7a 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -94,24 +94,16 @@ def self.method_visibility_for(object, method_name) # write `method_missing` in such a way that it handles a dynamic message # with private or protected visibility. Ruby doesn't provide you with # the caller info. - return :public if vis.nil? && object_responds_to?(object, method_name, false) - end - end - - # @api private - KERNEL_RESPOND_TO_METHOD = ::Kernel.instance_method(:respond_to?) + proxy = RSpec::Mocks.space.proxy_for(object) + respond_to = proxy.method_double_if_exists_for_message(:respond_to?) - if Support::RubyFeatures.supports_rebinding_module_methods? - def self.object_responds_to?(object, method_name, visibility) - KERNEL_RESPOND_TO_METHOD.bind(object).call(method_name, visibility) - end - else - def self.object_responds_to?(object, method_name, visibility) - if ::Kernel === object - KERNEL_RESPOND_TO_METHOD.bind(object).call(method_name, visibility) + if proxy && respond_to + object_responds_to_method_name = respond_to.original_method.call(method_name) else - object.respond_to?(method_name, visibility) + object_responds_to_method_name = object.respond_to?(method_name) end + + return :public if vis.nil? && object_responds_to_method_name end end end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index aa53e3066..c114dbfdc 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -228,6 +228,11 @@ def prepended_modules_of_singleton_class end end + # @private + def method_double_if_exists_for_message(message) + method_double_for(message) if @method_doubles.key?(message.to_sym) + end + private def method_double_for(message) From 36d86378711bf344a7018222845e2aec57957d71 Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Mon, 26 Oct 2015 09:32:52 +0200 Subject: [PATCH 0967/1179] Make RuboCop happy when run on rspec-mock test suite Fix more than 100 style warnings from RuboCop. --- Rakefile | 4 +- benchmarks/allocations/1_object_1000_mocks.rb | 2 +- benchmarks/allocations/helper.rb | 4 +- benchmarks/each_value_v_values_each.rb | 9 +- benchmarks/find_original_method_early.rb | 2 +- .../method_defined_at_any_visibility.rb | 6 +- benchmarks/thread_safety.rb | 2 +- benchmarks/transfer_nested_constants.rb | 2 +- .../step_definitions/additional_cli_steps.rb | 8 +- features/support/env.rb | 4 +- features/support/rubinius.rb | 2 +- spec/rspec/mocks/and_call_original_spec.rb | 4 +- spec/rspec/mocks/and_wrap_original_spec.rb | 2 +- spec/rspec/mocks/and_yield_spec.rb | 8 +- spec/rspec/mocks/any_instance_spec.rb | 14 +- spec/rspec/mocks/argument_matchers_spec.rb | 6 +- .../mocks/array_including_matcher_spec.rb | 2 +- spec/rspec/mocks/block_return_value_spec.rb | 4 +- ...bining_implementation_instructions_spec.rb | 6 +- spec/rspec/mocks/configuration_spec.rb | 2 +- spec/rspec/mocks/diffing_spec.rb | 22 ++- spec/rspec/mocks/double_spec.rb | 140 +++++++++--------- spec/rspec/mocks/example_methods_spec.rb | 4 +- .../mocks/matchers/have_received_spec.rb | 16 +- .../mocks/matchers/receive_messages_spec.rb | 8 +- spec/rspec/mocks/matchers/receive_spec.rb | 6 +- spec/rspec/mocks/mock_ordering_spec.rb | 1 - .../rspec/mocks/multiple_return_value_spec.rb | 6 +- spec/rspec/mocks/mutate_const_spec.rb | 5 +- spec/rspec/mocks/partial_double_spec.rb | 31 ++-- .../mocks/reraising_eager_raises_spec.rb | 6 +- spec/rspec/mocks/should_syntax_spec.rb | 19 ++- spec/rspec/mocks/spy_spec.rb | 2 +- spec/rspec/mocks/stash_spec.rb | 2 +- spec/rspec/mocks/stub_implementation_spec.rb | 10 +- spec/rspec/mocks/stub_spec.rb | 41 +++-- .../stubbed_message_expectations_spec.rb | 6 +- .../syntax_agnostic_message_matchers_spec.rb | 3 - spec/rspec/mocks/test_double_spec.rb | 2 +- spec/rspec/mocks/to_ary_spec.rb | 2 +- .../class_double_with_class_loaded_spec.rb | 2 +- .../instance_double_with_class_loaded_spec.rb | 5 +- .../mocks/verifying_doubles/naming_spec.rb | 6 +- spec/rspec/mocks_spec.rb | 3 +- spec/spec_helper.rb | 6 +- spec/support/doubled_classes.rb | 12 +- 46 files changed, 226 insertions(+), 233 deletions(-) diff --git a/Rakefile b/Rakefile index 492f66fb4..3258f0cf6 100644 --- a/Rakefile +++ b/Rakefile @@ -26,7 +26,7 @@ end namespace :clobber do desc "remove generated rbc files" task :rbc do - Dir['**/*.rbc'].each {|f| File.delete(f)} + Dir['**/*.rbc'].each { |f| File.delete(f) } end end @@ -40,7 +40,7 @@ with_changelog_in_features = lambda do |&block| end desc "Push docs/cukes to relishapp using the relish-client-gem" -task :relish, :version do |t, args| +task :relish, :version do |_t, args| raise "rake relish[VERSION]" unless args[:version] with_changelog_in_features.call do diff --git a/benchmarks/allocations/1_object_1000_mocks.rb b/benchmarks/allocations/1_object_1000_mocks.rb index 27c0ab60d..7c627d9b8 100644 --- a/benchmarks/allocations/1_object_1000_mocks.rb +++ b/benchmarks/allocations/1_object_1000_mocks.rb @@ -1,5 +1,5 @@ require_relative "helper" -symbols = (1..1000).map {|x| :"#{x}"} +symbols = (1..1000).map { |x| :"#{x}" } benchmark_allocations do o = Object.new diff --git a/benchmarks/allocations/helper.rb b/benchmarks/allocations/helper.rb index db00a8d17..a840452dd 100644 --- a/benchmarks/allocations/helper.rb +++ b/benchmarks/allocations/helper.rb @@ -3,7 +3,7 @@ require 'rspec/mocks/standalone' def benchmark_allocations(burn: 1) - stats = AllocationStats.new(burn: burn).trace do + stats = AllocationStats.new(:burn => burn).trace do yield end @@ -13,5 +13,5 @@ def benchmark_allocations(burn: 1) [:class_plus] end - puts stats.allocations(alias_paths: true).group_by(*columns).from_pwd.sort_by_size.to_text + puts stats.allocations(:alias_paths => true).group_by(*columns).from_pwd.sort_by_size.to_text end diff --git a/benchmarks/each_value_v_values_each.rb b/benchmarks/each_value_v_values_each.rb index cff19fd9a..450e5d062 100644 --- a/benchmarks/each_value_v_values_each.rb +++ b/benchmarks/each_value_v_values_each.rb @@ -1,8 +1,8 @@ require 'benchmark' -n = 10000 +n = 10_000 -m = 1.upto(1000).reduce({}) {|m, i| m[i] = i; m} +m = 1.upto(1000).inject({}) { |m, i| m[i] = i; m } Benchmark.benchmark do |bm| puts "#{n} times - ruby #{RUBY_VERSION}" @@ -13,7 +13,7 @@ 3.times do bm.report do n.times do - m.each_value {|v|} + m.each_value {} end end end @@ -24,7 +24,7 @@ 3.times do bm.report do n.times do - m.values.each{|v|} + m.values.each {} end end end @@ -43,7 +43,6 @@ # 0.910000 0.010000 0.920000 ( 0.909319) # 0.910000 0.000000 0.910000 ( 0.911225) - # $ ruby benchmarks/values_each_v_each_value.rb # 10000 times - ruby 2.0.0 # diff --git a/benchmarks/find_original_method_early.rb b/benchmarks/find_original_method_early.rb index 875ea9d02..5914dafa2 100644 --- a/benchmarks/find_original_method_early.rb +++ b/benchmarks/find_original_method_early.rb @@ -14,7 +14,7 @@ require 'benchmark' -n = 10000 +n = 10_000 Foo = Class.new(Object) do n.times do |i| diff --git a/benchmarks/method_defined_at_any_visibility.rb b/benchmarks/method_defined_at_any_visibility.rb index d538c2076..64e6e2af1 100644 --- a/benchmarks/method_defined_at_any_visibility.rb +++ b/benchmarks/method_defined_at_any_visibility.rb @@ -4,10 +4,10 @@ Foo = Class.new do 1.upto(n) do |i| - define_method(:"public_method_#{i}") { } - define_method(:"protected_method_#{i}") { } + define_method(:"public_method_#{i}") {} + define_method(:"protected_method_#{i}") {} protected :"protected_method_#{i}" - define_method(:"private_method_#{i}") { } + define_method(:"private_method_#{i}") {} private :"protected_method_#{i}" end end diff --git a/benchmarks/thread_safety.rb b/benchmarks/thread_safety.rb index 6ab24274d..e895fd8f1 100644 --- a/benchmarks/thread_safety.rb +++ b/benchmarks/thread_safety.rb @@ -7,7 +7,7 @@ bm.report("fetching a proxy") do RSpec::Mocks.with_temporary_scope do o = Object.new - 100000.times { + 100_000.times { RSpec::Mocks.space.proxy_for(o) } end diff --git a/benchmarks/transfer_nested_constants.rb b/benchmarks/transfer_nested_constants.rb index e46d42de3..3cfbf7384 100644 --- a/benchmarks/transfer_nested_constants.rb +++ b/benchmarks/transfer_nested_constants.rb @@ -3,7 +3,7 @@ require 'benchmark' require 'rspec/mocks' -N = ENV.fetch('N', 10000).to_i +N = ENV.fetch('N', 10_000).to_i M = ENV.fetch('M', 5).to_i puts "#{N} times, #{M} constants - ruby #{RUBY_VERSION}" diff --git a/features/step_definitions/additional_cli_steps.rb b/features/step_definitions/additional_cli_steps.rb index d9f3f7093..d5edc0a9f 100644 --- a/features/step_definitions/additional_cli_steps.rb +++ b/features/step_definitions/additional_cli_steps.rb @@ -1,10 +1,10 @@ Then /^the example(?:s)? should(?: all)? pass$/ do - step %q{the output should contain "0 failures"} - step %q{the exit status should be 0} + step %q(the output should contain "0 failures") + step %q(the exit status should be 0) end Then /^the examples should all fail, producing the following output:$/ do |table| - step %q{the exit status should be 1} + step %q(the exit status should be 1) examples, failures = all_output.match(/(\d+) examples?, (\d+) failures?/).captures.map(&:to_i) expect(examples).to be > 0 @@ -15,7 +15,7 @@ end Then /^it should fail with the following output:$/ do |table| - step %q{the exit status should be 1} + step %q(the exit status should be 1) lines = table.raw.flatten.reject(&:empty?) expect(all_output).to include(*lines) end diff --git a/features/support/env.rb b/features/support/env.rb index 21420d497..74a5e9517 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -10,13 +10,13 @@ end Aruba.configure do |config| - config.before_cmd do |cmd| + config.before_cmd do set_env('JRUBY_OPTS', "-X-C #{ENV['JRUBY_OPTS']}") # disable JIT since these processes are so short lived end end if RUBY_PLATFORM == 'java' Aruba.configure do |config| - config.before_cmd do |cmd| + config.before_cmd do set_env('RBXOPT', "-Xint=true #{ENV['RBXOPT']}") # disable JIT since these processes are so short lived end end if defined?(Rubinius) diff --git a/features/support/rubinius.rb b/features/support/rubinius.rb index 390796252..fc7fab2f9 100644 --- a/features/support/rubinius.rb +++ b/features/support/rubinius.rb @@ -1,6 +1,6 @@ # Required until https://github.com/rubinius/rubinius/issues/2430 is resolved ENV['RBXOPT'] = "#{ENV["RBXOPT"]} -Xcompiler.no_rbc" -Around "@unsupported-on-rbx" do |scenario, block| +Around "@unsupported-on-rbx" do |_scenario, block| block.call unless defined?(Rubinius) end diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index fdc0b2a5b..b0a01c8ff 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -101,7 +101,7 @@ def inst.foo; :bar; end expect(sub_sub_klass.foo).to eq(:bar) end - it "finds the method on the most direct ancestor even if the method " + + it "finds the method on the most direct ancestor even if the method " \ "is available on more distant ancestors" do klass.extend Module.new { def foo; :klass_bar; end } sub_klass = Class.new(klass) @@ -110,7 +110,7 @@ def inst.foo; :bar; end expect(sub_klass.foo).to eq(:sub_klass_bar) end - it "finds the method on the most direct singleton class ancestors even if the method " + + it "finds the method on the most direct singleton class ancestors even if the method " \ "is available on more distant ancestors" do klass.extend Module.new { def foo; :klass_bar; end } sub_klass = Class.new(klass) { def self.foo; :sub_klass_bar; end } diff --git a/spec/rspec/mocks/and_wrap_original_spec.rb b/spec/rspec/mocks/and_wrap_original_spec.rb index cf6d96ece..a69e3b4e9 100644 --- a/spec/rspec/mocks/and_wrap_original_spec.rb +++ b/spec/rspec/mocks/and_wrap_original_spec.rb @@ -57,7 +57,7 @@ def results it "can be constrained by specific arguments" do allow_it.to receive(:results) { :all } allow_it.to receive(:results).with(5).and_wrap_original { |m, n| m.call.first(n) } - expect(instance.results 5).to eq [1,2,3,4,5] + expect(instance.results 5).to eq [1, 2, 3, 4, 5] expect(instance.results).to eq :all end end diff --git a/spec/rspec/mocks/and_yield_spec.rb b/spec/rspec/mocks/and_yield_spec.rb index f902b3ee9..75cc4a979 100644 --- a/spec/rspec/mocks/and_yield_spec.rb +++ b/spec/rspec/mocks/and_yield_spec.rb @@ -20,7 +20,7 @@ it "evaluates the supplied block as it is read" do evaluated = false - allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| + allow(obj).to receive(:method_that_accepts_a_block).and_yield do |_eval_context| evaluated = true end expect(evaluated).to be_truthy @@ -145,17 +145,15 @@ expect(yielded_arg).to receive(:bar) end - obj.method_that_accepts_a_block do |obj| - # obj.bar is not called here + obj.method_that_accepts_a_block do |_obj| + # _obj.bar is not called here # foo is not called here end expect { verify configured_eval_context }.to fail expect { verify yielded_arg }.to fail end - end - end end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index b53c805f2..d9eb1cd1c 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -3,18 +3,18 @@ module RSpec module Mocks RSpec.describe "#any_instance" do - class CustomErrorForAnyInstanceSpec < StandardError;end + class CustomErrorForAnyInstanceSpec < StandardError; end let(:klass) do Class.new do def existing_method; :existing_method_return_value; end - def existing_method_with_arguments(arg_one, arg_two = nil); :existing_method_with_arguments_return_value; end + def existing_method_with_arguments(_a, _b=nil); :existing_method_with_arguments_return_value; end def another_existing_method; end - private + private def private_method; :private_method_return_value; end end end - let(:existing_method_return_value){ :existing_method_return_value } + let(:existing_method_return_value) { :existing_method_return_value } context "invocation order" do context "when stubbing" do @@ -310,13 +310,13 @@ def private_method; :private_method_return_value; end it "works with the non-standard constructor module" do allow_any_instance_of(Module).to receive(:foo).and_return(1) - module RSpec::SampleRspecTestModule;end + module RSpec::SampleRspecTestModule; end expect(RSpec::SampleRspecTestModule.foo).to eq(1) end it "works with the non-standard constructor class" do allow_any_instance_of(Class).to receive(:foo).and_return(1) - class RSpec::SampleRspecTestClass;end + class RSpec::SampleRspecTestClass; end expect(RSpec::SampleRspecTestClass.foo).to eq(1) end end @@ -1170,7 +1170,7 @@ def call(*args) it "doesn't fail when dup accepts parameters" do klazz = Class.new do - def dup(funky_option) + def dup(_) end end diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index f4f76fae7..3d4c15499 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -283,14 +283,14 @@ module Mocks it "fails array_including when args aren't array", :reset => true do expect(a_double).to receive(:msg).with(array_including(1, 2, 3)) expect { - a_double.msg(1, 2, 3) + a_double.msg(1, 2, 3) }.to fail_including "expected: (array_including(1, 2, 3))" end it "fails array_including when arg doesn't contain all elements", :reset => true do expect(a_double).to receive(:msg).with(array_including(1, 2, 3)) expect { - a_double.msg([1, 2]) + a_double.msg([1, 2]) }.to fail_including "expected: (array_including(1, 2, 3))" end end @@ -429,7 +429,7 @@ def ==(other) it "fails with zero arguments", :reset => true do expect do - expect(a_double).to receive(:msg).with {|arg| expect(arg).to eq :received } + expect(a_double).to receive(:msg).with { |arg| expect(arg).to eq :received } end.to raise_error(ArgumentError, /must have at least one argument/) end diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb index 1430f0391..f9a4495cf 100644 --- a/spec/rspec/mocks/array_including_matcher_spec.rb +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -43,7 +43,7 @@ module ArgumentMatchers context "failing" do it "fails when not all the entries in the expected are present" do - expect(array_including(1,2,3,4,5)).not_to be === [1,2] + expect(array_including(1, 2, 3, 4, 5)).not_to be === [1, 2] end it "fails when passed a composed matcher is pased and not satisfied" do diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 5a16ca143..121fb2cc8 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -27,7 +27,7 @@ it 'forwards all given args to the block, even when it receives a block' do obj = Object.new yielded_args = [] - allow(obj).to receive(:foo) { |*args, &bl| yielded_args << args } + allow(obj).to receive(:foo) { |*args, &_| yielded_args << args } obj.foo(1, 2, 3) expect(yielded_args).to eq([[1, 2, 3]]) @@ -43,7 +43,7 @@ it "returns the value of executing the block with given argument" do obj = Object.new - allow(obj).to receive(:foo).with('baz') {|x| 'bar' + x } + allow(obj).to receive(:foo).with('baz') { |x| 'bar' + x } expect(obj.foo('baz')).to eq('barbaz') end end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 3a9b0cba8..27d37cb9d 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -72,7 +72,7 @@ def verify_combined_implementation expect(dbl).to receive(:foo).twice { @block_called = true } end - the_double.foo { |a| } # to ensure it is called twice + the_double.foo { |_| } # to ensure it is called twice end it 'works when passing a block to `ordered`' do @@ -166,7 +166,7 @@ def verify_combined_implementation expect(RSpec).not_to receive(:warning) obj = Object.new - stub = allow(obj).to receive(:foo) { } + stub = allow(obj).to receive(:foo) {} stub.with(1) end @@ -191,7 +191,7 @@ def verify_combined_implementation expect { stubbed.and_raise("a") }.to raise_error(msg_fragment) expect { stubbed.and_throw(:bar) }.to raise_error(msg_fragment) - expect { stubbed.once { } }.to raise_error(msg_fragment) + expect { stubbed.once {} }.to raise_error(msg_fragment) expect(dbl.size).to eq(3) end diff --git a/spec/rspec/mocks/configuration_spec.rb b/spec/rspec/mocks/configuration_spec.rb index 4308b1220..dd1a00f7a 100644 --- a/spec/rspec/mocks/configuration_spec.rb +++ b/spec/rspec/mocks/configuration_spec.rb @@ -5,7 +5,7 @@ module Mocks let(:mod_1) { Module.new } let(:mod_2) { Module.new } - def instance_methods_of(mod) + def instance_methods_of(_mod) mod_1.instance_methods.map(&:to_sym) end diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index 23c8d054e..70f985fd7 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -33,7 +33,9 @@ expect(d).to receive(:foo).with("some string\nline2") expect { d.foo("this other string") - }.to fail_with("# received :foo with unexpected arguments\n expected: (\"some string\\nline2\")\n got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\n-line2\n+this other string\n") + }.to fail_with("# received :foo with unexpected arguments\n" \ + " expected: (\"some string\\nline2\")\n got: (\"this other string\")\n" \ + "Diff:\n@@ -1,3 +1,2 @@\n-some string\n-line2\n+this other string\n") end end @@ -42,7 +44,9 @@ expect(d).to receive(:foo).with("some string\nline2", "some other string") expect { d.foo("this other string") - }.to fail_with("# received :foo with unexpected arguments\n expected: (\"some string\\nline2\", \"some other string\")\n got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\\nline2\n-some other string\n+this other string\n") + }.to fail_with("# received :foo with unexpected arguments\n" \ + " expected: (\"some string\\nline2\", \"some other string\")\n" \ + " got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\\nline2\n-some other string\n+this other string\n") end end @@ -84,7 +88,7 @@ # has occasionally failed due to the output ordering varying between `inspect` # calls to the same hash. This regex allows us to work around that. def hash_regex_inspect(hash) - "\\{(#{hash.map { |key,value| "#{key.inspect}=>#{value.inspect}.*" }.join "|"}){#{hash.size}}\\}" + "\\{(#{hash.map { |key, value| "#{key.inspect}=>#{value.inspect}.*" }.join "|"}){#{hash.size}}\\}" end else def hash_regex_inspect(hash) @@ -111,7 +115,9 @@ def hash_regex_inspect(hash) expect(d).to receive(:foo).with(collab) expect { d.foo([]) - }.to fail_with("# received :foo with unexpected arguments\n expected: (#{collab_inspect})\n got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_inspect}]\n+[[]]\n") + }.to fail_with("# received :foo with unexpected arguments\n" \ + " expected: (#{collab_inspect})\n" \ + " got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_inspect}]\n+[[]]\n") end end end @@ -128,7 +134,9 @@ def hash_regex_inspect(hash) expect(d).to receive(:foo).with(collab) expect { d.foo([:a, :b]) - }.to fail_with("# received :foo with unexpected arguments\n expected: (#{collab_description})\n got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[\"#{collab_description}\"]\n+[[:a, :b]]\n") + }.to fail_with("# received :foo with unexpected arguments\n" \ + " expected: (#{collab_description})\n" \ + " got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[\"#{collab_description}\"]\n+[[:a, :b]]\n") end end end @@ -154,7 +162,9 @@ def inspect expect(d).to receive(:foo).with(collab) expect { d.foo([:a, :b]) - }.to fail_with("# received :foo with unexpected arguments\n expected: (#{collab_inspect})\n got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_pp}]\n+[[:a, :b]]\n") + }.to fail_with("# received :foo with unexpected arguments\n" \ + " expected: (#{collab_inspect})\n" \ + " got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_pp}]\n+[[:a, :b]]\n") end end end diff --git a/spec/rspec/mocks/double_spec.rb b/spec/rspec/mocks/double_spec.rb index 86d049853..ee4629c0a 100644 --- a/spec/rspec/mocks/double_spec.rb +++ b/spec/rspec/mocks/double_spec.rb @@ -195,63 +195,63 @@ module Mocks end it "allows parameter as return value" do - expect(@double).to receive(:something).with("a","b","c").and_return("booh") - expect(@double.something("a","b","c")).to eq "booh" + expect(@double).to receive(:something).with("a", "b", "c").and_return("booh") + expect(@double.something("a", "b", "c")).to eq "booh" verify @double end it "returns the previously stubbed value if no return value is set" do - allow(@double).to receive(:something).with("a","b","c").and_return(:stubbed_value) - expect(@double).to receive(:something).with("a","b","c") - expect(@double.something("a","b","c")).to eq :stubbed_value + allow(@double).to receive(:something).with("a", "b", "c").and_return(:stubbed_value) + expect(@double).to receive(:something).with("a", "b", "c") + expect(@double.something("a", "b", "c")).to eq :stubbed_value verify @double end it "returns nil if no return value is set and there is no previously stubbed value" do - expect(@double).to receive(:something).with("a","b","c") - expect(@double.something("a","b","c")).to be_nil + expect(@double).to receive(:something).with("a", "b", "c") + expect(@double.something("a", "b", "c")).to be_nil verify @double end it "raises exception if args don't match when method called" do - expect(@double).to receive(:something).with("a","b","c").and_return("booh") + expect(@double).to receive(:something).with("a", "b", "c").and_return("booh") expect { - @double.something("a","d","c") + @double.something("a", "d", "c") }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end describe "even when a similar expectation with different arguments exist" do it "raises exception if args don't match when method called, correctly reporting the offending arguments" do - expect(@double).to receive(:something).with("a","b","c").once - expect(@double).to receive(:something).with("z","x","c").once + expect(@double).to receive(:something).with("a", "b", "c").once + expect(@double).to receive(:something).with("z", "x", "c").once expect { - @double.something("a","b","c") - @double.something("z","x","g") + @double.something("a", "b", "c") + @double.something("z", "x", "g") }.to fail_with "# received :something with unexpected arguments\n expected: (\"z\", \"x\", \"c\")\n got: (\"z\", \"x\", \"g\")" end end it "raises exception if args don't match when method called even when the method is stubbed" do allow(@double).to receive(:something) - expect(@double).to receive(:something).with("a","b","c") + expect(@double).to receive(:something).with("a", "b", "c") expect { - @double.something("a","d","c") + @double.something("a", "d", "c") verify @double }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end it "raises exception if args don't match when method called even when using null_object" do @double = double("test double").as_null_object - expect(@double).to receive(:something).with("a","b","c") + expect(@double).to receive(:something).with("a", "b", "c") expect { - @double.something("a","d","c") + @double.something("a", "d", "c") verify @double }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end describe 'with a method that has a default argument' do it "raises an exception if the arguments don't match when the method is called, correctly reporting the offending arguments" do - def @double.method_with_default_argument(arg={}); end + def @double.method_with_default_argument(_={}); end expect(@double).to receive(:method_with_default_argument).with({}) expect { @@ -263,7 +263,7 @@ def @double.method_with_default_argument(arg={}); end it "fails if unexpected method called" do expect { - @double.something("a","b","c") + @double.something("a", "b", "c") }.to fail_with "# received unexpected message :something with (\"a\", \"b\", \"c\")" end @@ -289,13 +289,13 @@ def @double.method_with_default_argument(arg={}); end it "is wrappable in an array" do with_isolated_stderr do - expect( Array(@double) ).to eq([@double]) + expect(Array(@double)).to eq([@double]) end end it "is wrappable in an array when a null object" do with_isolated_stderr do - expect( Array(@double.as_null_object) ).to eq [@double] + expect(Array(@double.as_null_object)).to eq [@double] end end @@ -319,7 +319,7 @@ def @double.method_with_default_argument(arg={}); end end it "passes proc to expectation block with an argument" do - expect(@double).to receive(:foo) { |arg, &block| expect(block.call).to eq(:bar) } + expect(@double).to receive(:foo) { |_, &block| expect(block.call).to eq(:bar) } @double.foo(:arg) { :bar } end @@ -329,7 +329,7 @@ def @double.method_with_default_argument(arg={}); end end it "passes proc to stub block with an argument" do - allow(@double).to receive(:foo) { |arg, &block| expect(block.call).to eq(:bar) } + allow(@double).to receive(:foo) { |_, &block| expect(block.call).to eq(:bar) } @double.foo(:arg) { :bar } end @@ -407,7 +407,7 @@ def initialize(amount, units) end it "fails on no args if any args received" do - expect(@double).to receive(:something).with(no_args()) + expect(@double).to receive(:something).with(no_args) expect { @double.something 1 }.to fail_with "# received :something with unexpected arguments\n expected: (no args)\n got: (1)" @@ -428,99 +428,99 @@ def initialize(amount, units) end it "yields 0 args to blocks that take a variable number of arguments" do - expect(@double).to receive(:yield_back).with(no_args()).once.and_yield + expect(@double).to receive(:yield_back).with(no_args).once.and_yield a = nil - @double.yield_back {|*x| a = x} + @double.yield_back { |*x| a = x } expect(a).to eq [] verify @double end it "yields 0 args multiple times to blocks that take a variable number of arguments" do - expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield. + expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield. and_yield b = [] - @double.yield_back {|*a| b << a} - expect(b).to eq [ [], [] ] + @double.yield_back { |*a| b << a } + expect(b).to eq [[], []] verify @double end it "yields one arg to blocks that take a variable number of arguments" do - expect(@double).to receive(:yield_back).with(no_args()).once.and_yield(99) + expect(@double).to receive(:yield_back).with(no_args).once.and_yield(99) a = nil - @double.yield_back {|*x| a = x} + @double.yield_back { |*x| a = x } expect(a).to eq [99] verify @double end it "yields one arg 3 times consecutively to blocks that take a variable number of arguments" do - expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield(99). + expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield(99). and_yield(43). and_yield("something fruity") b = [] - @double.yield_back {|*a| b << a} + @double.yield_back { |*a| b << a } expect(b).to eq [[99], [43], ["something fruity"]] verify @double end it "yields many args to blocks that take a variable number of arguments" do - expect(@double).to receive(:yield_back).with(no_args()).once.and_yield(99, 27, "go") + expect(@double).to receive(:yield_back).with(no_args).once.and_yield(99, 27, "go") a = nil - @double.yield_back {|*x| a = x} + @double.yield_back { |*x| a = x } expect(a).to eq [99, 27, "go"] verify @double end it "yields many args 3 times consecutively to blocks that take a variable number of arguments" do - expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield(99, :green, "go"). + expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield(99, :green, "go"). and_yield("wait", :amber). and_yield("stop", 12, :red) b = [] - @double.yield_back {|*a| b << a} + @double.yield_back { |*a| b << a } expect(b).to eq [[99, :green, "go"], ["wait", :amber], ["stop", 12, :red]] verify @double end it "yields single value" do - expect(@double).to receive(:yield_back).with(no_args()).once.and_yield(99) + expect(@double).to receive(:yield_back).with(no_args).once.and_yield(99) a = nil - @double.yield_back {|x| a = x} + @double.yield_back { |x| a = x } expect(a).to eq 99 verify @double end it "yields single value 3 times consecutively" do - expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield(99). + expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield(99). and_yield(43). and_yield("something fruity") b = [] - @double.yield_back {|a| b << a} + @double.yield_back { |a| b << a } expect(b).to eq [99, 43, "something fruity"] verify @double end it "yields two values" do - expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + expect(@double).to receive(:yield_back).with(no_args).once.and_yield('wha', 'zup') a, b = nil - @double.yield_back {|x,y| a=x; b=y} + @double.yield_back { |x, y| a = x; b = y } expect(a).to eq 'wha' expect(b).to eq 'zup' verify @double end it "yields two values 3 times consecutively" do - expect(@double).to receive(:yield_back).once.with(no_args()).once.and_yield('wha', 'zup'). + expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield('wha', 'zup'). and_yield('not', 'down'). and_yield(14, 65) c = [] - @double.yield_back {|a,b| c << [a, b]} - expect(c).to eq [['wha', 'zup'], ['not', 'down'], [14, 65]] + @double.yield_back { |a, b| c << [a, b] } + expect(c).to eq [%w[wha zup], %w[not down], [14, 65]] verify @double end it "fails when calling yielding method with wrong arity" do - expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + expect(@double).to receive(:yield_back).with(no_args).once.and_yield('wha', 'zup') expect { - @double.yield_back {|a|} + @double.yield_back { |_| } }.to fail_with "# yielded |\"wha\", \"zup\"| to block with arity of 1" end @@ -534,17 +534,17 @@ def initialize(amount, units) end it "fails when calling yielding method consecutively with wrong arity" do - expect(@double).to receive(:yield_back).once.with(no_args()).and_yield('wha', 'zup'). + expect(@double).to receive(:yield_back).once.with(no_args).and_yield('wha', 'zup'). and_yield('down'). and_yield(14, 65) expect { c = [] - @double.yield_back {|a,b| c << [a, b]} + @double.yield_back { |a, b| c << [a, b] } }.to fail_with "# yielded |\"down\"| to block with arity of 2" end it "fails when calling yielding method without block" do - expect(@double).to receive(:yield_back).with(no_args()).once.and_yield('wha', 'zup') + expect(@double).to receive(:yield_back).with(no_args).once.and_yield('wha', 'zup') expect { @double.yield_back }.to fail_with "# asked to yield |[\"wha\", \"zup\"]| but no block was passed" @@ -560,7 +560,7 @@ def initialize(amount, units) expect(@double).to receive(:yield_me).and_yield 44 expect { - @double.yield_me do |x| + @double.yield_me do |_| raise "Bang" end }.to raise_error(StandardError, "Bang") @@ -578,10 +578,10 @@ def initialize(amount, units) end it "restores objects to their original state on rspec_reset" do - double = double("this is a double") - expect(double).to receive(:blah) - reset double - verify double #should throw if reset didn't work + dbl = double("this is a double") + expect(dbl).to receive(:blah) + reset dbl + verify dbl # should throw if reset didn't work end it "temporarily replaces a method stub on a double" do @@ -635,8 +635,8 @@ def initialize(amount, units) it "does not mess with the stub's yielded values when also doubleed" do allow(@double).to receive(:yield_back).and_yield(:stub_value) expect(@double).to receive(:yield_back).and_yield(:double_value) - @double.yield_back{|v| expect(v).to eq :double_value } - @double.yield_back{|v| expect(v).to eq :stub_value } + @double.yield_back { |v| expect(v).to eq :double_value } + @double.yield_back { |v| expect(v).to eq :stub_value } verify @double end @@ -651,8 +651,8 @@ def initialize(amount, units) end it "assigns stub return values" do - double = RSpec::Mocks::Double.new('name', :message => :response) - expect(double.message).to eq :response + dbl = RSpec::Mocks::Double.new('name', :message => :response) + expect(dbl.message).to eq :response end describe "a double message receiving a block" do @@ -662,7 +662,7 @@ def initialize(amount, units) end def add_call - @calls = @calls + 1 + @calls += 1 end it "supports a block passed to `receive` for `expect`" do @@ -720,16 +720,16 @@ def add_call describe 'string representation generated by #to_s' do it 'does not contain < because that might lead to invalid HTML in some situations' do - double = double("Dog") - valid_html_str = "#{double}" + dbl = double("Dog") + valid_html_str = "#{dbl}" expect(valid_html_str).not_to include('<') end end describe "#to_str", :unless => RUBY_VERSION == '1.9.2' do it "should not respond to #to_str to avoid being coerced to strings by the runtime" do - double = double("Foo") - expect { double.to_str }.to raise_error( + dbl = double("Foo") + expect { dbl.to_str }.to raise_error( RSpec::Mocks::MockExpectationError, '# received unexpected message :to_str with (no args)') end @@ -737,14 +737,14 @@ def add_call describe "double created with no name" do it "does not use a name in a failure message" do - double = double() - expect {double.foo}.to raise_error.with_message(a_string_including("# received")) + dbl = double + expect { dbl.foo }.to raise_error.with_message(a_string_including("# received")) end it "does respond to initially stubbed methods" do - double = double(:foo => "woo", :bar => "car") - expect(double.foo).to eq "woo" - expect(double.bar).to eq "car" + dbl = double(:foo => "woo", :bar => "car") + expect(dbl.foo).to eq "woo" + expect(dbl.bar).to eq "car" end end diff --git a/spec/rspec/mocks/example_methods_spec.rb b/spec/rspec/mocks/example_methods_spec.rb index 59c526524..36bb71623 100644 --- a/spec/rspec/mocks/example_methods_spec.rb +++ b/spec/rspec/mocks/example_methods_spec.rb @@ -1,8 +1,8 @@ module RSpec module Mocks RSpec.describe ExampleMethods do - it 'does not define private helper methods since it gets included into a ' + - 'namespace where users define methods and could inadvertently overwrite ' + + it 'does not define private helper methods since it gets included into a ' \ + 'namespace where users define methods and could inadvertently overwrite ' \ 'them' do expect(ExampleMethods.private_instance_methods).to eq([]) end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index cbeeff4fc..a75bcd95a 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -84,7 +84,7 @@ module Mocks expect(dbl).to have_received(:foo) { |*args| yielded << args } - _expect(yielded).to include([:a,:b,:c]) + _expect(yielded).to include([:a, :b, :c]) end it "takes a do-end block and yields the arguments given to the stubbed method call" do @@ -94,7 +94,7 @@ module Mocks expect(dbl).to have_received(:foo) do |*args| yielded << args end - _expect(yielded).to include([:a,:b,:c]) + _expect(yielded).to include([:a, :b, :c]) end it "passes if expectations against the yielded arguments pass" do @@ -117,7 +117,7 @@ module Mocks }.to raise_error(RSpec::Expectations::ExpectationNotMetError) end - it 'gives precedence to a `{ ... }` block when both forms are provided ' + + it 'gives precedence to a `{ ... }` block when both forms are provided ' \ 'since that form actually binds to `receive`' do dbl = double(:foo => nil) called = [] @@ -131,7 +131,7 @@ module Mocks it 'forwards any block passed during method invocation to the `have_received` block' do dbl = spy - block = lambda { } + block = lambda {} dbl.foo(&block) expect(dbl).to have_received(:foo) do |&passed_block| @@ -242,7 +242,7 @@ module Mocks _expect { expect(dbl).to have_received(:expected_method).with(:four, :four).once }.to fail_including("expected: (:four, :four)", - "got:","(:one, :four) (2 times)", + "got:", "(:one, :four) (2 times)", "(:two, :four) (1 time)", "(:three, :four) (3 times)") end @@ -264,7 +264,7 @@ module Mocks _expect { expect(dbl).to have_received(:expected_method).with(:one, :four).once }.to fail_including("expected: (:one, :four)", - "got:","([:one], :four)", + "got:", "([:one], :four)", "(:one, [:four])") end @@ -274,7 +274,7 @@ module Mocks _expect { expect(dbl).to have_received(:expected_method).with(:one, :two, :three).once - }.to fail_including("expected: (:one, :two, :three)", "got:","(:one, :one, :two)") + }.to fail_including("expected: (:one, :two, :three)", "got:", "(:one, :one, :two)") end end end @@ -601,7 +601,7 @@ module Mocks end end - %w(exactly at_least at_most times once twice).each do |constraint| + %w[exactly at_least at_most times once twice].each do |constraint| it "does not allow #{constraint} to be used because it creates confusion" do dbl = double_with_unmet_expectation(:expected_method) _expect { diff --git a/spec/rspec/mocks/matchers/receive_messages_spec.rb b/spec/rspec/mocks/matchers/receive_messages_spec.rb index 4c6e729e8..c6ea801f2 100644 --- a/spec/rspec/mocks/matchers/receive_messages_spec.rb +++ b/spec/rspec/mocks/matchers/receive_messages_spec.rb @@ -124,28 +124,28 @@ module Mocks example "allow(...).to_not receive_messages(:a => 1, :b => 2)" do expect { allow(obj).to_not receive_messages(:a => 1, :b => 2) }.to( - raise_error "`allow(...).to_not receive_messages` is not supported "+ + raise_error "`allow(...).to_not receive_messages` is not supported " \ "since it doesn't really make sense. What would it even mean?" ) end example "allow_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do expect { allow_any_instance_of(obj).to_not receive_messages(:a => 1, :b => 2) }.to( - raise_error "`allow_any_instance_of(...).to_not receive_messages` is not supported "+ + raise_error "`allow_any_instance_of(...).to_not receive_messages` is not supported " \ "since it doesn't really make sense. What would it even mean?" ) end example "expect(...).to_not receive_messages(:a => 1, :b => 2)" do expect { expect(obj).to_not receive_messages(:a => 1, :b => 2) }.to( - raise_error "`expect(...).to_not receive_messages` is not supported "+ + raise_error "`expect(...).to_not receive_messages` is not supported " \ "since it doesn't really make sense. What would it even mean?" ) end example "expect_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do expect { expect_any_instance_of(obj).to_not receive_messages(:a => 1, :b => 2) }.to( - raise_error "`expect_any_instance_of(...).to_not receive_messages` is not supported "+ + raise_error "`expect_any_instance_of(...).to_not receive_messages` is not supported " \ "since it doesn't really make sense. What would it even mean?" ) end diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index bedd70b38..d0fb4a75b 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -75,7 +75,7 @@ module Mocks expect(receiver.foo).to eq(5) end - it 'gives precedence to a `{ ... }` block when both forms are provided ' + + it 'gives precedence to a `{ ... }` block when both forms are provided ' \ 'since that form actually binds to `receive`' do wrapped.to receive(:foo) { :curly } do :do_end @@ -198,7 +198,7 @@ module Mocks it 'allows the caller to constrain the received arguments' do wrapped.not_to receive(:foo).with(:a) - def receiver.method_missing(*a); end # a poor man's stub... + def receiver.method_missing(*); end # a poor man's stub... expect { receiver.foo(:b) @@ -491,7 +491,7 @@ def receiver.method_missing(*a); end # a poor man's stub... Class.new do include RSpec::Mocks::ExampleMethods - def eq(value) + def eq(_) double("MyMatcher") end end diff --git a/spec/rspec/mocks/mock_ordering_spec.rb b/spec/rspec/mocks/mock_ordering_spec.rb index d4eebb799..c0727b666 100644 --- a/spec/rspec/mocks/mock_ordering_spec.rb +++ b/spec/rspec/mocks/mock_ordering_spec.rb @@ -1,6 +1,5 @@ module RSpec module Mocks - RSpec.describe "ordering" do before { @double = double("test double") } after { reset @double } diff --git a/spec/rspec/mocks/multiple_return_value_spec.rb b/spec/rspec/mocks/multiple_return_value_spec.rb index a83ae305c..2c5737955 100644 --- a/spec/rspec/mocks/multiple_return_value_spec.rb +++ b/spec/rspec/mocks/multiple_return_value_spec.rb @@ -16,8 +16,8 @@ module Mocks RSpec.describe "a message expectation with multiple return values and no specified count" do before(:each) do @double = double - @return_values = [1,2,3] - expect(@double).to receive(:do_something).and_return(@return_values[0],@return_values[1],@return_values[2]) + @return_values = [1, 2, 3] + expect(@double).to receive(:do_something).and_return(@return_values[0], @return_values[1], @return_values[2]) end it "returns values in order" do @@ -53,7 +53,7 @@ module Mocks RSpec.describe "a message expectation with multiple return values with a specified count equal to the number of values" do before(:each) do @double = double - @return_values = [1,2,3] + @return_values = [1, 2, 3] expect(@double).to receive(:do_something).exactly(3).times.and_return(@return_values[0], @return_values[1], @return_values[2]) end diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index bb9bb7a29..7786dd637 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -152,7 +152,6 @@ def change_const_value_to(value) it_behaves_like "loaded constant hiding", "TestClassThatDefinesSend::C" end - context 'for a loaded nested constant' do it_behaves_like "loaded constant hiding", "TestClass::Nested" end @@ -206,8 +205,8 @@ def change_const_value_to(value) hide_const("TOP_LEVEL_VALUE_CONST") expect(recursive_const_defined?("TOP_LEVEL_VALUE_CONST")).to be_falsey - stub_const("TOP_LEVEL_VALUE_CONST", 12345) - expect(TOP_LEVEL_VALUE_CONST).to eq 12345 + stub_const("TOP_LEVEL_VALUE_CONST", 12_345) + expect(TOP_LEVEL_VALUE_CONST).to eq 12_345 reset_rspec_mocks expect(TOP_LEVEL_VALUE_CONST).to eq orig_value diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 6bf979e94..9606f3a89 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -109,10 +109,10 @@ module Mocks it "uses reports nil in the error message" do allow_message_expectations_on_nil - _nil = nil - expect(_nil).to receive(:foobar) + nil_var = nil + expect(nil_var).to receive(:foobar) expect { - verify _nil + verify nil_var }.to raise_error( RSpec::Mocks::MockExpectationError, %Q|(nil).foobar(*(any args))\n expected: 1 time with any arguments\n received: 0 times with any arguments| @@ -138,15 +138,15 @@ def self.inspect expect(object).to receive(:foobar) twin = object.clone twin.foobar - expect{ verify twin }.not_to raise_error - expect{ verify object }.not_to raise_error + expect { verify twin }.not_to raise_error + expect { verify object }.not_to raise_error end it "clears message expectations when `dup`ed" do expect(object).to receive(:foobar) duplicate = object.dup - expect{ duplicate.foobar }.to raise_error(NoMethodError, /foobar/) - expect{ verify object }.to fail_with(/foobar/) + expect { duplicate.foobar }.to raise_error(NoMethodError, /foobar/) + expect { verify object }.to fail_with(/foobar/) end end @@ -249,7 +249,6 @@ def ==(other) end RSpec.describe "A partial class mock that has been subclassed" do - let(:klass) { Class.new } let(:subklass) { Class.new(klass) } @@ -284,9 +283,9 @@ def public_method private_method protected_method end - protected + protected def protected_method; end - private + private def private_method; end end end @@ -310,7 +309,6 @@ def private_method; end expect(object.protected_methods).to include_method(:protected_method) object.public_method end - end RSpec.describe 'when verify_partial_doubles configuration option is set' do @@ -322,7 +320,7 @@ def implemented "works" end - def initialize(a, b) + def initialize(_a, _b) end def respond_to?(method_name, include_all=false) @@ -337,7 +335,7 @@ def method_missing(method_name, *args) end end - private + private def defined_private_method "works" @@ -384,7 +382,7 @@ def defined_private_method end it 'avoids deadlocks when a proxy is accessed from within a `before_verifying_doubles` callback' do - _klass = Class.new { def message; end; } + msg_klass = Class.new { def message; end; } called_for = [] RSpec.configuration.mock_with(:rspec) do |config| @@ -396,7 +394,7 @@ def defined_private_method end end - expect { allow(_klass.new).to receive(:message) }.to_not raise_error + expect { allow(msg_klass.new).to receive(:message) }.to_not raise_error end context "for a class" do @@ -469,7 +467,6 @@ def defined_private_method }.to yield_with_args(have_attributes :target => klass) end - it 'does not allow a non-existing method to be called on any_instance' do prevents(/does not implement/) { expect_any_instance_of(klass).to receive(:unimplemented) @@ -519,7 +516,7 @@ def defined_private_method context "on a class that has redefined `new`" do it "uses the method signature of the redefined `new` for arg verification" do subclass = Class.new(klass) do - def self.new(a); end + def self.new(_); end end prevents(/arguments/) { allow(subclass).to receive(:new).with(1, 2) } diff --git a/spec/rspec/mocks/reraising_eager_raises_spec.rb b/spec/rspec/mocks/reraising_eager_raises_spec.rb index c69919d74..6071c7607 100644 --- a/spec/rspec/mocks/reraising_eager_raises_spec.rb +++ b/spec/rspec/mocks/reraising_eager_raises_spec.rb @@ -34,7 +34,7 @@ # Prepare a failing expectation for a different message expect(dbl).not_to receive(:bar) - RSpec::Support.with_failure_notifier(Proc.new { }) { dbl.bar } + RSpec::Support.with_failure_notifier(Proc.new {}) { dbl.bar } }.not_to notify_with_same_source_id_as_later_verification end end @@ -95,7 +95,7 @@ def prepare(dbl) end context "when an expectation is called out of order", - :pending => "Says bar was called 0 times when it was, see: http://git.io/pjTq" do + :pending => "Says bar was called 0 times when it was, see: http://git.io/pjTq" do it "reraises during verification" do with_unfulfilled_double do |dbl| expect(dbl).to receive(:foo).ordered @@ -150,7 +150,7 @@ def verify_all_source_id def capture_notified_source_id(&block) source_id = nil - notifier = Proc.new { |err, opt| source_id = opt.fetch(:source_id) } + notifier = Proc.new { |_err, opt| source_id = opt.fetch(:source_id) } RSpec::Support.with_failure_notifier(notifier, &block) source_id end diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index 6337b8d12..d9de00399 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -16,13 +16,13 @@ end it "answers true for received_message? when message received with correct args" do - dbl.message 1,2,3 - expect(dbl.received_message?(:message, 1,2,3)).to be_truthy + dbl.message 1, 2, 3 + expect(dbl.received_message?(:message, 1, 2, 3)).to be_truthy end it "answers false for received_message? when message received with incorrect args" do - dbl.message 1,2,3 - expect(dbl.received_message?(:message, 1,2)).to be_falsey + dbl.message 1, 2, 3 + expect(dbl.received_message?(:message, 1, 2)).to be_falsey end end @@ -163,7 +163,7 @@ def use_rspec_mocks verify mock rescue Exception => e ensure - expect(e.backtrace.to_s).to match(/\/path\/to\/blah.ext:37/m) + expect(e.backtrace.to_s).to match(%r{/path/to/blah.ext:37}m) end end @@ -215,9 +215,9 @@ def use_rspec_mocks let(:klass) do Class.new do def existing_method; :existing_method_return_value; end - def existing_method_with_arguments(arg_one, arg_two = nil); :existing_method_with_arguments_return_value; end + def existing_method_with_arguments(_a, _b=nil); :existing_method_with_arguments_return_value; end def another_existing_method; end - private + private def private_method; :private_method_return_value; end end end @@ -466,7 +466,7 @@ def use_rspec_mocks let(:expected_arguments) { [ /Using.*without explicitly enabling/, - {:replacement=>"the new `:expect` syntax or explicitly enable `:should`"} + {:replacement => "the new `:expect` syntax or explicitly enable `:should`"} ] } @@ -500,7 +500,7 @@ def use_rspec_mocks it "warns about unstubbing once, regardless of how many times it is called" do expect(RSpec).to receive(:deprecate).with(/Using.*without explicitly enabling/, - {:replacement => "`allow(...).to receive(...).and_call_original` or explicitly enable `:should`"}) + :replacement => "`allow(...).to receive(...).and_call_original` or explicitly enable `:should`") o = Object.new o2 = Object.new @@ -511,7 +511,6 @@ def use_rspec_mocks o2.unstub(:faces) end - it "doesn't warn about stubbing after a reset and setting should" do expect(RSpec).not_to receive(:deprecate) RSpec::Mocks.configuration.reset_syntaxes_to_default diff --git a/spec/rspec/mocks/spy_spec.rb b/spec/rspec/mocks/spy_spec.rb index 0921e506c..49ce160b6 100644 --- a/spec/rspec/mocks/spy_spec.rb +++ b/spec/rspec/mocks/spy_spec.rb @@ -33,7 +33,7 @@ end it "records called methods" do - expect(subject.tap { |s| s.foo}).to have_received(:foo) + expect(subject.tap { |s| s.foo }).to have_received(:foo) end it 'fails fast when `have_received` is passed an undefined method name' do diff --git a/spec/rspec/mocks/stash_spec.rb b/spec/rspec/mocks/stash_spec.rb index 93a202995..51c1f483a 100644 --- a/spec/rspec/mocks/stash_spec.rb +++ b/spec/rspec/mocks/stash_spec.rb @@ -3,7 +3,7 @@ module Mocks RSpec.describe "only stashing the original method" do let(:klass) do Class.new do - def self.foo(arg) + def self.foo(_) :original_value end end diff --git a/spec/rspec/mocks/stub_implementation_spec.rb b/spec/rspec/mocks/stub_implementation_spec.rb index 9507f3830..2c3cb9327 100644 --- a/spec/rspec/mocks/stub_implementation_spec.rb +++ b/spec/rspec/mocks/stub_implementation_spec.rb @@ -3,7 +3,7 @@ module Mocks RSpec.describe "stub implementation" do describe "with no args" do it "execs the block when called" do - obj = double() + obj = double allow(obj).to receive(:foo) { :bar } expect(obj.foo).to eq :bar end @@ -11,16 +11,16 @@ module Mocks describe "with one arg" do it "execs the block with that arg when called" do - obj = double() - allow(obj).to receive(:foo) {|given| given} + obj = double + allow(obj).to receive(:foo) { |given| given } expect(obj.foo(:bar)).to eq :bar end end describe "with variable args" do it "execs the block when called" do - obj = double() - allow(obj).to receive(:foo) {|*given| given.first} + obj = double + allow(obj).to receive(:foo) { |*given| given.first } expect(obj.foo(:bar)).to eq :bar end end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index ebde9681d..37a59d9e0 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -8,7 +8,8 @@ def existing_class_method existing_private_class_method end - private + private + def existing_private_class_method :original_value end @@ -18,7 +19,6 @@ def existing_instance_method existing_private_instance_method end - private def existing_private_instance_method :original_value end @@ -81,7 +81,7 @@ def existing_private_instance_method it "is cleared when stubbed object when `dup`ed" do allow(@stub).to receive(:foobar).and_return(1) - expect{ @stub.dup.foobar }.to raise_error NoMethodError, /foobar/ + expect { @stub.dup.foobar }.to raise_error NoMethodError, /foobar/ end context "using `with`" do @@ -205,7 +205,7 @@ def object.value; :original; end object.singleton_class.class_eval do def value; :original; end prepend ToBePrepended - prepend Module.new { } + prepend Module.new {} end expect(object.value).to eq :original_prepended @@ -322,21 +322,21 @@ class << self; public :hello; end; it "correctly handles stubbing inherited mixed in class methods" do mod = Module.new do - def method_a - raise "should not execute method_a" - end + def method_a + raise "should not execute method_a" + end - def self.included(other) - other.extend self - end - end + def self.included(other) + other.extend self + end + end a = Class.new { include mod } b = Class.new(a) do - def self.method_b - "executed method_b" - end - end + def self.method_b + "executed method_b" + end + end allow(a).to receive(:method_a) allow(b).to receive(:method_b).and_return("stubbed method_b") @@ -414,14 +414,14 @@ def object.existing_method; :original_value; end end it "returns values in order to consecutive calls" do - allow(@instance).to receive(:msg).and_return("1",2,:three) + allow(@instance).to receive(:msg).and_return("1", 2, :three) expect(@instance.msg).to eq("1") expect(@instance.msg).to eq(2) expect(@instance.msg).to eq(:three) end it "keeps returning last value in consecutive calls" do - allow(@instance).to receive(:msg).and_return("1",2,:three) + allow(@instance).to receive(:msg).and_return("1", 2, :three) expect(@instance.msg).to eq("1") expect(@instance.msg).to eq(2) expect(@instance.msg).to eq(:three) @@ -432,7 +432,7 @@ def object.existing_method; :original_value; end it "yields a specified object" do allow(@instance).to receive(:method_that_yields).and_yield(:yielded_obj) current_value = :value_before - @instance.method_that_yields {|val| current_value = val} + @instance.method_that_yields { |val| current_value = val } expect(current_value).to eq :yielded_obj verify @instance end @@ -441,7 +441,7 @@ def object.existing_method; :original_value; end allow(@instance).to receive(:method_that_yields_multiple_times).and_yield(:yielded_value). and_yield(:another_value) current_value = [] - @instance.method_that_yields_multiple_times {|val| current_value << val} + @instance.method_that_yields_multiple_times { |val| current_value << val } expect(current_value).to eq [:yielded_value, :another_value] verify @instance end @@ -501,7 +501,7 @@ def object.existing_method; :original_value; end }.to raise_error(/received :foo with unexpected arguments/) end - it "complains if called with other arg", :github_issue => [123,147] do + it "complains if called with other arg", :github_issue => [123, 147] do expect { @stub.foo("other") }.to raise_error(/received :foo with unexpected arguments.*Please stub a default value/m) @@ -535,6 +535,5 @@ def object.existing_method; :original_value; end expect(@stub.bar).to eq(15) end end - end end diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index dac373a4e..2181133ee 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -16,10 +16,10 @@ }.to raise_error( RSpec::Mocks::MockExpectationError, a_string_including( - %Q|# received :foo with unexpected arguments|, + %Q(# received :foo with unexpected arguments), "expected: (\"first\")", - "got:","(\"second\")", - "(\"third\")")) + "got:", "(\"second\")", + "(\"third\")")) reset dbl end diff --git a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb index b91515f9a..057d6e541 100644 --- a/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +++ b/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb @@ -1,6 +1,5 @@ module RSpec module Mocks - RSpec.describe ".allow_message" do let(:subject) { Object.new } @@ -92,8 +91,6 @@ module Mocks verify subject end - end - end end diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index a260d35dc..945066091 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -10,7 +10,7 @@ module Mocks end it "gives the correct call site for the warning" do - expect_warning_with_call_site(__FILE__, __LINE__+1) + expect_warning_with_call_site(__FILE__, __LINE__ + 1) subject.freeze end diff --git a/spec/rspec/mocks/to_ary_spec.rb b/spec/rspec/mocks/to_ary_spec.rb index 3a9833ae8..4b060afb7 100644 --- a/spec/rspec/mocks/to_ary_spec.rb +++ b/spec/rspec/mocks/to_ary_spec.rb @@ -41,7 +41,7 @@ expect(obj).not_to respond_to(:to_ary) end - it "doesn't respond to to_a", :if => ( RUBY_VERSION.to_f > 1.8 ) do + it "doesn't respond to to_a", :if => (RUBY_VERSION.to_f > 1.8) do expect(obj).not_to respond_to(:to_a) end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index e03d85c55..f12dac635 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -141,7 +141,7 @@ module Mocks context "on a class that has redefined `new`" do it "uses the method signature of the redefined `new` for arg verification" do klass = Class.new(LoadedClass) do - def self.new(a); end + def self.new(_); end end o = class_double(klass) diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index 8ff35853e..5455fcaa9 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -80,8 +80,7 @@ module Mocks o = instance_double('LoadedClass', :defined_instance_method => 25) expect { o.defined_instance_method(:a) - }.to raise_error(ArgumentError, - "Wrong number of arguments. Expected 0, got 1.") + }.to raise_error(ArgumentError, "Wrong number of arguments. Expected 0, got 1.") end if required_kw_args_supported? @@ -131,7 +130,7 @@ module Mocks end end - it 'checks that stubbed methods with required keyword args are ' + + it 'checks that stubbed methods with required keyword args are ' \ 'invoked with the required arguments' do o = instance_double('LoadedClass', :kw_args_method => true) expect { diff --git a/spec/rspec/mocks/verifying_doubles/naming_spec.rb b/spec/rspec/mocks/verifying_doubles/naming_spec.rb index 0acd82fbc..0a15b28fe 100644 --- a/spec/rspec/mocks/verifying_doubles/naming_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/naming_spec.rb @@ -5,7 +5,7 @@ module Mocks RSpec::Matchers.define :fail_expectations_as do |expected| description { "include a meaningful name in the exception" } - def error_message_for(verifying_double) + def error_message_for(_) expect(actual).to have_received(:defined_instance_and_class_method) rescue MockExpectationError, Expectations::ExpectationNotMetError => e e.message @@ -14,8 +14,8 @@ def error_message_for(verifying_double) end failure_message do |actual| - "expected #{actual.inspect} to fail expectations as:\n" + - " #{expected.inspect}, but failed with:\n" + + "expected #{actual.inspect} to fail expectations as:\n" \ + " #{expected.inspect}, but failed with:\n" \ " #{@error_message.inspect}" end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 94d78438f..ab8243e0f 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -15,7 +15,8 @@ # to prevent the loading of securerandom by faking out `BasicObject#__id__ lib_preamble.unshift "class BasicObject; def __id__; end; end" if RUBY_VERSION == '1.9.2' - it_behaves_like 'library wide checks', 'rspec-mocks', :preamble_for_lib => lib_preamble, + it_behaves_like 'library wide checks', 'rspec-mocks', + :preamble_for_lib => lib_preamble, :allowed_loaded_feature_regexps => [ /rbconfig/ # loaded by rspec-support ] do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0c563fb2d..8d791dcf6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -54,7 +54,7 @@ def expect_fast_failure_from(double, *fail_with_args, &blk) end module VerificationHelpers - def prevents(msg = //, &block) + def prevents(msg=//, &block) expect(&block).to fail_with msg end end @@ -67,7 +67,7 @@ def self.fake_matcher_description extend RSpec::Matchers::DSL matcher :fake_matcher do |expected| - match {|actual| actual == expected} + match { |actual| actual == expected } description do MatcherHelpers.fake_matcher_description @@ -75,7 +75,6 @@ def self.fake_matcher_description end end - require 'rspec/support/spec' RSpec.configure do |config| @@ -134,7 +133,6 @@ def self.fake_matcher_description end end - RSpec.shared_context "with isolated configuration" do orig_configuration = nil before do diff --git a/spec/support/doubled_classes.rb b/spec/support/doubled_classes.rb index 3150c9f29..eb2132c0e 100644 --- a/spec/support/doubled_classes.rb +++ b/spec/support/doubled_classes.rb @@ -5,12 +5,11 @@ class LoadedClass N = :n INSTANCE = LoadedClass.new - def initialize(a, b) + def initialize(_a, _b) end class << self - - def respond_to?(method_name, include_all = false) + def respond_to?(method_name, include_all=false) return true if method_name == :dynamic_class_method super end @@ -34,16 +33,15 @@ def defined_protected_class_method def defined_private_class_method end - end def defined_instance_method end - def instance_method_with_two_args(a, b) + def instance_method_with_two_args(_a, _b) end - def instance_method_with_only_defaults(a=1, b=2) + def instance_method_with_only_defaults(_a=1, _b=2) end def defined_instance_and_class_method @@ -63,7 +61,7 @@ def mixed_args_method(foo, bar, optional_arg_1:1, optional_arg_2:2) def send(*) end - def respond_to?(method_name, include_all = false) + def respond_to?(method_name, include_all=false) return true if method_name == :dynamic_instance_method super end From fea1c8143f986161d8a86521996bbb32f992fd83 Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Fri, 23 Oct 2015 08:28:33 +0200 Subject: [PATCH 0968/1179] Avoid exception when class method is stubbed in subclass on MRI 1.8.7 Singleton methods cannot be rebound in MRI 1.8.7, even to a subclass. This means that when a singleton method is defined on a superclass, and then stubbed out on a subclass, the code used to cache the original method (so it can be used by 'and_call_original', etc) doesn't work. Actually, it doesn't just 'not work' -- it raises a TypeError which causes the example to fail, *even if* the original method would never have been used. Catch this exception, and use an alternate implementation instead which works even on MRI 1.8.7. Unfortunately, this requires us to expose some implementation details of MethodDouble. Also add detailed specs which codify the expected behavior of 'receive' when used on the same class method in both a superclass and subclass. --- lib/rspec/mocks/instance_method_stasher.rb | 1 - lib/rspec/mocks/method_double.rb | 2 +- lib/rspec/mocks/proxy.rb | 24 +++++++++++--- spec/rspec/mocks/and_call_original_spec.rb | 13 ++++++++ spec/rspec/mocks/matchers/receive_spec.rb | 38 ++++++++++++++++++++++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index 44145ca08..0af2e8cd5 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -31,7 +31,6 @@ def stash def stashed_method_name "obfuscated_by_rspec_mocks__#{@method}" end - private :stashed_method_name # @private def restore diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 53e6efd7a..939af9829 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -3,7 +3,7 @@ module Mocks # @private class MethodDouble # @private - attr_reader :method_name, :object, :expectations, :stubs + attr_reader :method_name, :object, :expectations, :stubs, :method_stasher # @private def initialize(object, method_name, proxy) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index aa53e3066..7a17bc5fb 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -371,20 +371,36 @@ def original_method_handle_for(message) return super unless unbound_method unbound_method.bind(object) + # :nocov: + rescue TypeError + if RUBY_VERSION == '1.8.7' + # In MRI 1.8.7, a singleton method on a class cannot be rebound to its subclass + if unbound_method && unbound_method.owner.ancestors.first != unbound_method.owner + # This is a singleton method; we can't do anything with it + # But we can work around this using a different implementation + double = method_double_from_ancestor_for(message) + return object.method(double.method_stasher.stashed_method_name) + end + end + raise + # :nocov: end protected def original_unbound_method_handle_from_ancestor_for(message) - method_double = @method_doubles.fetch(message) do + double = method_double_from_ancestor_for(message) + double && double.original_method.unbind + end + + def method_double_from_ancestor_for(message) + @method_doubles.fetch(message) do # The fact that there is no method double for this message indicates # that it has not been redefined by rspec-mocks. We need to continue # looking up the ancestor chain. return superclass_proxy && - superclass_proxy.original_unbound_method_handle_from_ancestor_for(message) + superclass_proxy.method_double_from_ancestor_for(message) end - - method_double.original_method.unbind end def superclass_proxy diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index fdc0b2a5b..67562fe4d 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -143,6 +143,19 @@ def inst.foo; :bar; end expect(subclass.new_instance).to be_a(subclass) end + context 'when a class method is stubbed in the superclass' do + it 'still works for class methods defined on a superclass' do + superclass = Class.new { def self.foo; "foo"; end } + subclass = Class.new(superclass) + + allow(superclass).to receive(:foo).and_return(:fake) + expect(subclass).to receive(:foo).and_call_original + + expect(superclass.foo).to be :fake + expect(subclass.foo).to eq "foo" + end + end + it 'works for class methods defined on a grandparent class' do sub_subclass = Class.new(Class.new(klass)) expect(sub_subclass).to receive(:new_instance).and_call_original diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index bedd70b38..8b50fa7f9 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -267,6 +267,44 @@ def receiver.method_missing(*a); end # a poor man's stub... it_behaves_like "resets partial mocks cleanly" do let(:target) { allow(object) } end + + context 'on a class method, from a class with subclasses' do + let(:superclass) { Class.new { def self.foo; "foo"; end }} + let(:subclass_redef) { Class.new(superclass) { def self.foo; ".foo."; end }} + let(:subclass_deleg) { Class.new(superclass) { def self.foo; super.upcase; end }} + let(:subclass_asis) { Class.new(superclass) } + + context 'if the method is redefined in the subclass' do + it 'does not stub the method in the subclass' do + allow(superclass).to receive(:foo) { "foo!!" } + expect(superclass.foo).to eq "foo!!" + expect(subclass_redef.foo).to eq ".foo." + end + end + + context 'if the method is not redefined in the subclass' do + it 'stubs the method in the subclass' do + allow(superclass).to receive(:foo) { "foo!!" } + expect(superclass.foo).to eq "foo!!" + expect(subclass_asis.foo).to eq "foo!!" + end + end + + it 'creates stub which can be called using `super` in a subclass' do + allow(superclass).to receive(:foo) { "foo!!" } + expect(subclass_deleg.foo).to eq "FOO!!" + end + + it 'can stub the same method simultaneously in the superclass and subclasses' do + allow(subclass_redef).to receive(:foo) { "__foo__" } + allow(superclass).to receive(:foo) { "foo!!" } + allow(subclass_deleg).to receive(:foo) { "$$foo$$" } + + expect(subclass_redef.foo).to eq "__foo__" + expect(superclass.foo).to eq "foo!!" + expect(subclass_deleg.foo).to eq "$$foo$$" + end + end end describe "allow(...).not_to receive" do From 9853907a3c328ce01d7f453605c07dcc0a17185d Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Fri, 23 Oct 2015 10:37:41 +0200 Subject: [PATCH 0969/1179] Fix typos in spec for 'and_call_original' --- spec/rspec/mocks/and_call_original_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index 67562fe4d..b9befeaf2 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -30,7 +30,7 @@ def self.new_instance end end - context "when a non-existant method has been stubbed previously" do + context "when a non-existent method has been stubbed previously" do it 'restores the original NameError behavior' do expect { instance.abcd }.to raise_error(NameError).with_message(/abcd/) @@ -249,7 +249,7 @@ def self.method let(:request) { request_klass.new(:get, "http://foo.com/bar") } - it 'still works even though #method has been overriden' do + it 'still works even though #method has been overridden' do expect(request).to receive(:perform).and_call_original expect(request.perform).to eq(:the_response) end From ec0772127d056e4ffdbaaa23009e8131251b6b8b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 27 Oct 2015 10:35:05 +1100 Subject: [PATCH 0970/1179] changelog for #1026 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index ba56f136e..76fa62d38 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,8 @@ Bug Fixes: was received is forwarded to the `have_received` block. (Myron Marston, #1006) * Fix infinite loop in error generator when stubbing `respond_to?`. (Alex Dowad, #1022) +* Fix issue with using `receive` on subclasses (at a class level) with 1.8.7. + (Alex Dowad, #1026) ### 3.3.2 / 2015-07-15 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.1...v3.3.2) From e0943f3423c9ba84a64f83500c579133792557d5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 27 Oct 2015 10:42:49 +1100 Subject: [PATCH 0971/1179] Refactor to use original respond_to definition internally --- lib/rspec/mocks/method_reference.rb | 39 ++++++++++++------------- spec/rspec/mocks/partial_double_spec.rb | 3 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 0831abb7a..844ee71a2 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -85,26 +85,25 @@ class << self end def self.method_visibility_for(object, method_name) - instance_method_visibility_for(class << object; self; end, method_name).tap do |vis| - # If the method is not defined on the class, `instance_method_visibility_for` - # returns `nil`. However, it may be handled dynamically by `method_missing`, - # so here we check `respond_to` (passing false to not check private methods). - # - # This only considers the public case, but I don't think it's possible to - # write `method_missing` in such a way that it handles a dynamic message - # with private or protected visibility. Ruby doesn't provide you with - # the caller info. - proxy = RSpec::Mocks.space.proxy_for(object) - respond_to = proxy.method_double_if_exists_for_message(:respond_to?) - - if proxy && respond_to - object_responds_to_method_name = respond_to.original_method.call(method_name) - else - object_responds_to_method_name = object.respond_to?(method_name) - end - - return :public if vis.nil? && object_responds_to_method_name - end + vis = instance_method_visibility_for(class << object; self; end, method_name) + + # If the method is not defined on the class, `instance_method_visibility_for` + # returns `nil`. However, it may be handled dynamically by `method_missing`, + # so here we check `respond_to` (passing false to not check private methods). + # + # This only considers the public case, but I don't think it's possible to + # write `method_missing` in such a way that it handles a dynamic message + # with private or protected visibility. Ruby doesn't provide you with + # the caller info. + return vis unless vis.nil? + + proxy = RSpec::Mocks.space.proxy_for(object) + respond_to = proxy.method_double_if_exists_for_message(:respond_to?) + + visible = respond_to && respond_to.original_method.call(method_name) || + object.respond_to?(method_name) + + return :public if visible end end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 643344ebd..d11e9bbcc 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -54,7 +54,8 @@ def call(name) an_object = the_klass.new - expect(an_object).to receive(:respond_to?).with(:my_method) { true } + expect(an_object).to receive(:respond_to?) + .with(:my_method).at_least(:once) { true } expect(an_object).to receive(:my_method) an_object.call :my_method From e744a977a148e36f4d897dbf30ae8ee86f69fa89 Mon Sep 17 00:00:00 2001 From: georgeu2000 Date: Tue, 3 Nov 2015 14:54:39 -0800 Subject: [PATCH 0972/1179] Fix typo. --- features/basics/null_object_doubles.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/basics/null_object_doubles.feature b/features/basics/null_object_doubles.feature index b2b99533f..6e5a62c34 100644 --- a/features/basics/null_object_doubles.feature +++ b/features/basics/null_object_doubles.feature @@ -3,7 +3,7 @@ Feature: Null object doubles [Test doubles](./test-doubles) are strict by default, raising errors when they receive messages that have not been allowed or expected. You can chain `as_null_object` off of `double` in order to make the double "loose". For any message that has not explicitly allowed or expected, the double - will return itself. It acts as a block-hole null object, allowing arbitrarily deep method chains. + will return itself. It acts as a black hole null object, allowing arbitrarily deep method chains. Scenario: `as_null_object` allows arbitrarily deep message chains and returns itself Given a file named "as_null_object_spec.rb" with: From ad03a31ab61a845550684e5de0274b2568dc2a9c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 11 Nov 2015 23:45:40 -0800 Subject: [PATCH 0973/1179] Update changelog for v3.4.0 [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 76fa62d38..c64b8de03 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.4.0 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.2...master) +### 3.4.0 / 2015-11-11 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.2...v3.4.0) Enhancements: From b528144417b2653bd388654fd6acf91a625ab3d8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 11 Nov 2015 23:46:41 -0800 Subject: [PATCH 0974/1179] Release v3.4.0 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index dfe47386b..4ce636a07 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.4.0.pre' + STRING = '3.4.0' end end end From dcb77cd040286bb5c5c099a20d53102ca9471683 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 12 Nov 2015 00:02:10 -0800 Subject: [PATCH 0975/1179] Bump version to 3.5.0.pre [ci skip] --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 4ce636a07..00e9b7b3e 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.4.0' + STRING = '3.5.0.pre' end end end From f09ac9ffa1a780056b4df1c5b78a6ebbdb70ce16 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 13 Nov 2015 23:06:07 -0800 Subject: [PATCH 0976/1179] Fix project source dirs. --- spec/spec_helper.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8d791dcf6..d3f51dd93 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -117,6 +117,13 @@ def self.fake_matcher_description # we do something that makes these specs pass, we are notified. config.filter_run_excluding :ordered_and_vague_counts_unsupported unless ENV['CI'] + # We don't want rspec-core to look in our `lib` for failure snippets. + # When it does that, it inevitably finds this line: + # `RSpec::Support.notify_failure(*args)` + # ...which isn't very helpful. Far better for it to find the expectation + # call site in the spec. + config.project_source_dirs -= %w[ lib ] + RSpec::Matchers.define_negated_matcher :a_string_excluding, :include end From bcd9c668ca18bfb50ef2e5f3165bae07e547653c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 13 Nov 2015 23:18:20 -0800 Subject: [PATCH 0977/1179] Fix cukes to match latest rspec-core formatting. --- features/basics/expecting_messages.feature | 2 ++ features/basics/spies.feature | 2 ++ features/configuring_responses/raising_an_error.feature | 1 + features/old_syntax/should_receive.feature | 2 ++ 4 files changed, 7 insertions(+) diff --git a/features/basics/expecting_messages.feature b/features/basics/expecting_messages.feature index e885f9509..5d095ef84 100644 --- a/features/basics/expecting_messages.feature +++ b/features/basics/expecting_messages.feature @@ -19,6 +19,7 @@ Feature: Expecting messages """ 1) An unfulfilled positive message expectation triggers a failure Failure/Error: expect(dbl).to receive(:foo) + (Double "Some Collaborator").foo(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments @@ -54,6 +55,7 @@ Feature: Expecting messages """ 1) A negative message expectation fails when the message is received Failure/Error: dbl.foo + (Double "Some Collaborator").foo(no args) expected: 0 times with any arguments received: 1 time diff --git a/features/basics/spies.feature b/features/basics/spies.feature index a6fe39f71..7de9a6430 100644 --- a/features/basics/spies.feature +++ b/features/basics/spies.feature @@ -71,6 +71,7 @@ Feature: Spies """ 1) failure when the message has not been received for a spy Failure/Error: expect(invitation).to have_received(:deliver) + (Double "invitation").deliver(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments @@ -79,6 +80,7 @@ Feature: Spies """ 2) failure when the message has not been received for a partial double Failure/Error: expect(Invitation).to have_received(:deliver) + (Invitation (class)).deliver(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments diff --git a/features/configuring_responses/raising_an_error.feature b/features/configuring_responses/raising_an_error.feature index c8d212ada..5d4487807 100644 --- a/features/configuring_responses/raising_an_error.feature +++ b/features/configuring_responses/raising_an_error.feature @@ -23,6 +23,7 @@ Feature: Raising an error """ 1) Making it raise an error raises the provided exception Failure/Error: dbl.foo + RuntimeError: boom """ diff --git a/features/old_syntax/should_receive.feature b/features/old_syntax/should_receive.feature index 5e7b4d95d..9307c6b1b 100644 --- a/features/old_syntax/should_receive.feature +++ b/features/old_syntax/should_receive.feature @@ -36,6 +36,7 @@ Feature: `should_receive` """ 1) An unfulfilled message expectation triggers a failure Failure/Error: dbl.should_receive(:foo) + (Double "Some Collaborator").foo(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments @@ -71,6 +72,7 @@ Feature: `should_receive` """ 1) A negative message expectation fails when the message is received Failure/Error: dbl.foo + (Double "Some Collaborator").foo(no args) expected: 0 times with any arguments received: 1 time From c040d47a3b9d99e6079c0f2e871fa89e504aa5df Mon Sep 17 00:00:00 2001 From: Bruno Bonamin Date: Tue, 17 Nov 2015 17:29:31 +0000 Subject: [PATCH 0978/1179] Fixes typo on `receive_message_chain` --- features/working_with_legacy_code/message_chains.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/working_with_legacy_code/message_chains.feature b/features/working_with_legacy_code/message_chains.feature index f7158e323..754f48cdf 100644 --- a/features/working_with_legacy_code/message_chains.feature +++ b/features/working_with_legacy_code/message_chains.feature @@ -38,7 +38,7 @@ Feature: Message Chains implementation calls `foo.baz.bar`, the stub will not work. - Chaining with `recieve_message_chain` creates ambiguity in how the chains should + Chaining with `receive_message_chain` creates ambiguity in how the chains should be applied and applies design pressure on complex interactions in the implementation code. As such `receive_message_chain` is not a perfect replacement for `receive`. (see [Issue 921](https://github.com/rspec/rspec-mocks/issues/921) for a more detailed From 54ede04da9a27cde6f55e80f2d004707d393cdc3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 7 Dec 2015 22:01:05 +1100 Subject: [PATCH 0979/1179] Updated common markdown files (from rspec-dev) [ci skip] --- BUILD_DETAIL.md | 2 +- CODE_OF_CONDUCT.md | 55 ++++++++++++++++++++++++++++++---------------- CONTRIBUTING.md | 4 ++-- DEVELOPMENT.md | 2 +- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/BUILD_DETAIL.md b/BUILD_DETAIL.md index 6a3bf7675..127d9e13d 100644 --- a/BUILD_DETAIL.md +++ b/BUILD_DETAIL.md @@ -1,5 +1,5 @@ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a761f5255..2585a1eea 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,5 +1,5 @@ @@ -10,33 +10,50 @@ values contributions from anyone, the RSpec project has adopted the following code of conduct. All contributors and participants (including maintainers!) are expected to abide by its terms. -As contributors and maintainers of this project, we pledge to respect all -people who contribute through reporting issues, posting feature requests, -updating documentation, submitting pull requests or patches, and other -activities. +As contributors and maintainers of this project, and in the interest of +fostering an open and welcoming community, we pledge to respect all people who +contribute through reporting issues, posting feature requests, updating +documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, -body size, race, ethnicity, age, religion or similar personal characteristic. +body size, race, ethnicity, age, religion, or nationality. -Examples of unacceptable behavior by participants include, but are not limited -to, the use of sexual language or imagery, derogatory comments or personal -attacks, trolling, public or private harassment, insults, or other -unprofessional conduct. +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, such as physical or electronic + addresses, without explicit permission +* Other unethical or unprofessional conduct Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct. Project maintainers who do not -follow the Code of Conduct may be removed from the project team. +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +By adopting this Code of Conduct, project maintainers commit themselves to +fairly and consistently applying these principles to every aspect of managing +this project. Project maintainers who do not follow or enforce the Code of +Conduct may be permanently removed from the project team. -This code of conduct applies both within project spaces and in public spaces +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by opening an issue or contacting one or more of the project -maintainers. - -This Code of Conduct is adapted from the [Contributor -Covenant](http://contributor-covenant.org), version 1.1.0, available at -[http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) +reported by contacting one of the project maintainers listed at +http://rspec.info/about/. All complaints will be reviewed and investigated +and will result in a response that is deemed necessary and appropriate to the +circumstances. Maintainers are obligated to maintain confidentiality with +regard to the reporter of an incident. + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.3.0, available at +[http://contributor-covenant.org/version/1/3/0/][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/3/0/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94cddf3b6..7f714cdb2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,11 @@ # Contributing -RSpec is a community-driven project that has benefited from improvements from over over *500* contributors. +RSpec is a community-driven project that has benefited from improvements from over *500* contributors. We welcome contributions from *everyone*. While contributing, please follow the project [code of conduct](CODE_OF_CONDUCT.md), so that everyone can be included. If you'd like to help make RSpec better, here are some ways you can contribute: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 9908ab231..9e31a0521 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,5 +1,5 @@ From 149d1cb827174a46ef3d37faa806d586d71f2563 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Thu, 31 Dec 2015 02:51:03 +0900 Subject: [PATCH 0980/1179] Undefine original method in InstanceMethodStasher#stash ... to handle the method redefinition warning on MRI 2.3: ``` /Users/me/Projects/rspec-dev/repos/rspec-mocks/lib/rspec/mocks/method_double.rb:63: warning: method redefined; discarding old running_in_drb? /Users/me/Projects/rspec-dev/repos/rspec-core/lib/rspec/core/runner.rb:150: warning: previous definition of running_in_drb? was here ``` For some reason MRI 2.2 does not warn of method redefinition when the original method handle has been taken with Object#method before the redefinition. On the other hand, MRI 2.3 warns of it regardless of whether the original method handle has been taken or not. ``` obj = Object.new def obj.foo :original_foo end obj.method(:foo) if ARGV.first == 'take_method_handle' singleton_klass = class << obj; self; end singleton_klass.class_exec do define_method(:foo) { } end ``` ``` $ ruby -v -w test.rb ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin14] test.rb:11: warning: method redefined; discarding old foo test.rb:3: warning: previous definition of foo was here $ ruby -v -w test.rb take_method_handle ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin14] $ ruby -v -w test.rb ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14] test.rb:11: warning: method redefined; discarding old foo test.rb:3: warning: previous definition of foo was here $ ruby -v -w test.rb take_method_handle ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14] test.rb:11: warning: method redefined; discarding old foo test.rb:3: warning: previous definition of foo was here ``` Previously we have left the original method as is in InstanceMethodStasher#stash, because it would soon be overwritten in MethodDouble#define_proxy_method. However now MRI 2.3 warns of it, we should properly undefine the original method before redefining it. --- lib/rspec/mocks/instance_method_stasher.rb | 1 + spec/rspec/mocks/instance_method_stasher_spec.rb | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index 0af2e8cd5..13e502a47 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -54,6 +54,7 @@ def method_is_stashed? def stash return unless method_defined_directly_on_klass? @original_method ||= ::RSpec::Support.method_handle_for(@object, @method) + @klass.__send__(:undef_method, @method) end # @private diff --git a/spec/rspec/mocks/instance_method_stasher_spec.rb b/spec/rspec/mocks/instance_method_stasher_spec.rb index ac2eadb92..e0614cc0f 100644 --- a/spec/rspec/mocks/instance_method_stasher_spec.rb +++ b/spec/rspec/mocks/instance_method_stasher_spec.rb @@ -60,12 +60,19 @@ def obj.hello; :overridden_hello; end; def obj.hello; :hello_defined_on_singleton_class; end; stashed_method = stasher_for(obj, :hello) + stashed_method.stash + expect(obj.methods.grep(/rspec/)).to eq([]) + end - expect { - stashed_method.stash - }.not_to change { obj.methods.count } + it "undefines the original method", :if => (RUBY_VERSION.to_f > 1.8) do + obj = Object.new + def obj.hello; :hello_defined_on_singleton_class; end; - expect(obj.methods.grep(/rspec/)).to eq([]) + stashed_method = stasher_for(obj, :hello) + stashed_method.stash + + expect(obj.methods).not_to include(:hello) + expect(obj).not_to respond_to(:hello) end end end From cac68620ccfd40fccefed49fbadd4e2d22e554e7 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Mon, 4 Jan 2016 21:06:21 +0900 Subject: [PATCH 0981/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 ++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 8 +------- script/predicate_functions.sh | 14 +------------- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 26 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index b2dd87abb..c6f89446a 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. +# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index b21b3185d..767c93e30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. +# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -19,6 +19,7 @@ rvm: - 2.0.0 - 2.1 - 2.2 + - 2.3.0 - ruby-head - ree - rbx diff --git a/appveyor.yml b/appveyor.yml index 001e1bddd..9108caca4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. +# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 5ae735d27..965017c52 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. +# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index ce3ffca3f..79589bfe0 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. +# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -11,12 +11,6 @@ export JRUBY_OPTS=${JRUBY_OPTS:-"--server -Xcompile.invokedynamic=false"} SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` -# Don't allow rubygems to pollute what's loaded. Also, things boot faster -# without the extra load time of rubygems. Only works on MRI Ruby 1.9+ -if is_mri_192_plus; then - export RUBYOPT="--disable=gem" -fi - function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index a7d95d817..0f756605a 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. +# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -33,18 +33,6 @@ function is_mri_192 { fi } -function is_mri_192_plus { - if is_mri; then - if ruby -e "exit(RUBY_VERSION.to_f > 1.8)"; then - return 0 - else - return 1 - fi - else - return 1 - fi -} - function is_mri_2plus { if is_mri; then if ruby -e "exit(RUBY_VERSION.to_f > 2.0)"; then diff --git a/script/run_build b/script/run_build index c508ef29f..3558d9753 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. +# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index bc12598d0..cdf3f433a 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-10-13T18:52:08-07:00 from the rspec-dev repo. +# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From b94fe10e34ececd8cda34c480d996f036a58c557 Mon Sep 17 00:00:00 2001 From: Joe Rafaniello Date: Fri, 18 Dec 2015 15:18:31 -0500 Subject: [PATCH 0982/1179] Only restore an instance method on the owning class Save the the original method owner before we back it up so we can only restore the method for the owning class. Fixes #1042 ```ruby class Shape def area(value) value end end class Circle < Shape def area super(2) end end class SmallCircle < Circle end ``` When using: allow_any_instance_of(SmallCircle).to receive(:area) Previously, we'd backup the area method from the first superclass that implements it. In this case, we'd copy Circle#area into SmallCircle with a backup name. Once this method is copied into SmallCircle, we lose the original ownership of Circle since SmallCircle now implements the method. At the end of the example, we remove the stubbing and rename the copied method back, causing SmallCircle to implement area when it never did previously. Any calls to SmallCircle#area would end up calling the contents of Circle#area, which calls super(2) on the Circle class, not Shape. Because Circle#area doesn't take any arguments, you get an ArgumentError. Previously, ruby 2.2 and below allowed this strange behavior to work, because it skipped calling the same method in super via this commit: https://github.com/ruby/ruby/commit/c8854d2ca4be9ee6946e6d17b0e17d9ef130ee81 --- lib/rspec/mocks/any_instance/recorder.rb | 10 +++++++--- spec/rspec/mocks/any_instance_spec.rb | 25 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 943728ad5..8651e02ef 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -18,6 +18,7 @@ def initialize(klass) @stubs = Hash.new { |hash, key| hash[key] = [] } @observed_methods = [] @played_methods = {} + @backed_up_method_owner = {} @klass = klass @expectation_set = false end @@ -190,9 +191,9 @@ def restore_original_method!(method_name) return unless @klass.instance_method(method_name).owner == @klass alias_method_name = build_alias_method_name(method_name) - @klass.class_exec do + @klass.class_exec(@backed_up_method_owner) do |backed_up_method_owner| remove_method method_name - alias_method method_name, alias_method_name + alias_method method_name, alias_method_name if backed_up_method_owner[method_name.to_sym] == self remove_method alias_method_name end end @@ -204,10 +205,13 @@ def remove_dummy_method!(method_name) end def backup_method!(method_name) + return unless public_protected_or_private_method_defined?(method_name) + alias_method_name = build_alias_method_name(method_name) + @backed_up_method_owner[method_name.to_sym] ||= @klass.instance_method(method_name).owner @klass.class_exec do alias_method alias_method_name, method_name - end if public_protected_or_private_method_defined?(method_name) + end end def public_protected_or_private_method_defined?(method_name) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index d9eb1cd1c..d5cb9b4cb 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -1,5 +1,22 @@ require 'delegate' +module AnyInstanceSpec + class GrandparentClass + def foo(_a) + 'bar' + end + end + + class ParentClass < GrandparentClass + def foo + super(:a) + end + end + + class ChildClass < ParentClass + end +end + module RSpec module Mocks RSpec.describe "#any_instance" do @@ -1225,6 +1242,14 @@ def dup(_) expect(instance.existing_method).to eq :existing_method_return_value end + it "does not restore a stubbed method not originally implemented in the class" do + allow_any_instance_of(::AnyInstanceSpec::ChildClass).to receive(:foo).and_return :result + expect(::AnyInstanceSpec::ChildClass.new.foo).to eq :result + + reset_all + expect(::AnyInstanceSpec::ChildClass.new.foo).to eq 'bar' + end + it "restores the original behaviour, even if the expectation fails" do expect_any_instance_of(klass).to receive(:existing_method).with(1).and_return(:stubbed_return_value) From f531ae6c0d2360131c61230d490f46d23de5c70b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 6 Jan 2016 22:57:44 +1100 Subject: [PATCH 0983/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 14 ++++++++++++-- script/predicate_functions.sh | 14 +++++++++++++- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 31 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index c6f89446a..53d476e33 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. +# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 767c93e30..811457aef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. +# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 9108caca4..eaaba1c15 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. +# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 965017c52..706eea4de 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. +# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 79589bfe0..36f5f094c 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. +# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -11,6 +11,12 @@ export JRUBY_OPTS=${JRUBY_OPTS:-"--server -Xcompile.invokedynamic=false"} SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` +# Don't allow rubygems to pollute what's loaded. Also, things boot faster +# without the extra load time of rubygems. Only works on MRI Ruby 1.9+ +if is_mri_192_plus; then + export RUBYOPT="--disable=gem" +fi + function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" @@ -77,7 +83,11 @@ function run_spec_suite_for { pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - travis_retry eval "bundle install $bundle_install_flags" + if is_mri_192_plus; then + travis_retry eval "RUBYOPT=$RUBYOPT:'--enable rubygems' bundle install $bundle_install_flags" + else + travis_retry eval "bundle install $bundle_install_flags" + fi run_specs_and_record_done popd else diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 0f756605a..e3149cd17 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. +# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -33,6 +33,18 @@ function is_mri_192 { fi } +function is_mri_192_plus { + if is_mri; then + if ruby -e "exit(RUBY_VERSION.to_f > 1.8)"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + function is_mri_2plus { if is_mri; then if ruby -e "exit(RUBY_VERSION.to_f > 2.0)"; then diff --git a/script/run_build b/script/run_build index 3558d9753..f5303e14e 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. +# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index cdf3f433a..f1f8704a0 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-04T21:06:21+09:00 from the rspec-dev repo. +# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 7369e6b949585114bc1e9f20885d64a65f95cc31 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 6 Jan 2016 09:33:24 -0800 Subject: [PATCH 0984/1179] Changelog for #1043. --- Changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.md b/Changelog.md index c64b8de03..db6d2129b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,10 @@ +### Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.0...master) + +Bug Fixes: + +* Fix `any_instance` to work properly on Ruby 2.3. (Joe Rafaniello, #1043) + ### 3.4.0 / 2015-11-11 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.2...v3.4.0) From eae0606af439f81db374e824fa57a51daccc0985 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 10 Jan 2016 15:59:28 -0800 Subject: [PATCH 0985/1179] 3.4.1 release notes. [ci skip] --- Changelog.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index db6d2129b..9862b7d4e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.0...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.1...master) + +### 3.4.1 / 2016-01-10 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.0...v3.4.1) Bug Fixes: From 655b1182bbfce2fcbd1551ef7e14c570cef33d7c Mon Sep 17 00:00:00 2001 From: Patrik Wenger Date: Mon, 11 Jan 2016 23:39:47 +0100 Subject: [PATCH 0986/1179] fix typo --- features/verifying_doubles/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifying_doubles/README.md b/features/verifying_doubles/README.md index d27acacb7..5d2970859 100644 --- a/features/verifying_doubles/README.md +++ b/features/verifying_doubles/README.md @@ -10,4 +10,4 @@ number of arguments. This dual approach allows you to move very quickly and test components in isolation, while giving you confidence that your doubles are not a complete fiction. Testing in isolation is -optional but recommend for classes that do not depend on third-party components. +optional but recommended for classes that do not depend on third-party components. From 4beb2880eda0e76a8e4ce97cac66880fe26748b4 Mon Sep 17 00:00:00 2001 From: "Andrew Kozin (aka nepalez)" Date: Sat, 16 Jan 2016 14:01:48 +0300 Subject: [PATCH 0987/1179] Allow inheriting mocks matchers With reference to #1055 Allows rspec matchers inherited from `RSpec::Mocks::Matchers::*` to be used both in mocks and expectations: class ReceiveFoo < RSpec::Mocks::Matchers::Receive def initialize super :foo, nil end end describe "receiving foo" do it "works" do object = double allow(object).to receive_foo expect(object).to receive_foo object.foo end end --- lib/rspec/mocks.rb | 1 + lib/rspec/mocks/matchers/have_received.rb | 2 ++ lib/rspec/mocks/matchers/matcher.rb | 8 ++++++++ lib/rspec/mocks/matchers/receive.rb | 2 ++ lib/rspec/mocks/matchers/receive_message_chain.rb | 2 ++ lib/rspec/mocks/matchers/receive_messages.rb | 2 ++ lib/rspec/mocks/targets.rb | 2 +- spec/rspec/mocks/matchers/receive_spec.rb | 6 ++++++ 8 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 lib/rspec/mocks/matchers/matcher.rb diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 17b2831bc..738a72df3 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -117,6 +117,7 @@ class << self # Namespace for mock-related matchers. module Matchers + autoload :Matcher, "rspec/mocks/matchers/matcher" autoload :HaveReceived, "rspec/mocks/matchers/have_received" autoload :Receive, "rspec/mocks/matchers/receive" autoload :ReceiveMessageChain, "rspec/mocks/matchers/receive_message_chain" diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index ec3248c40..caa7e20b9 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -3,6 +3,8 @@ module Mocks module Matchers # @private class HaveReceived + include Matcher + COUNT_CONSTRAINTS = %w[exactly at_least at_most times once twice thrice] ARGS_CONSTRAINTS = %w[with] CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w[ordered] diff --git a/lib/rspec/mocks/matchers/matcher.rb b/lib/rspec/mocks/matchers/matcher.rb new file mode 100644 index 000000000..19414b870 --- /dev/null +++ b/lib/rspec/mocks/matchers/matcher.rb @@ -0,0 +1,8 @@ +module RSpec + module Mocks + module Matchers + # just a "tag" for rspec-mock matchers detection + module Matcher; end + end + end +end diff --git a/lib/rspec/mocks/matchers/receive.rb b/lib/rspec/mocks/matchers/receive.rb index a16b28fb3..980a01dde 100644 --- a/lib/rspec/mocks/matchers/receive.rb +++ b/lib/rspec/mocks/matchers/receive.rb @@ -5,6 +5,8 @@ module Mocks module Matchers # @private class Receive + include Matcher + def initialize(message, block) @message = message @block = block diff --git a/lib/rspec/mocks/matchers/receive_message_chain.rb b/lib/rspec/mocks/matchers/receive_message_chain.rb index d831cd3ac..583ecf70a 100644 --- a/lib/rspec/mocks/matchers/receive_message_chain.rb +++ b/lib/rspec/mocks/matchers/receive_message_chain.rb @@ -5,6 +5,8 @@ module Mocks module Matchers # @private class ReceiveMessageChain + include Matcher + def initialize(chain, &block) @chain = chain @block = block diff --git a/lib/rspec/mocks/matchers/receive_messages.rb b/lib/rspec/mocks/matchers/receive_messages.rb index b6316aa45..218d238e3 100644 --- a/lib/rspec/mocks/matchers/receive_messages.rb +++ b/lib/rspec/mocks/matchers/receive_messages.rb @@ -3,6 +3,8 @@ module Mocks module Matchers # @private class ReceiveMessages + include Matcher + def initialize(message_return_value_hash) @message_return_value_hash = message_return_value_hash @backtrace_line = CallerFilter.first_non_rspec_line diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 5777810b9..61375b101 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -38,7 +38,7 @@ def self.disallow_negation(method_name) private def matcher_allowed?(matcher) - matcher.class.name.start_with?("RSpec::Mocks::Matchers".freeze) + Matchers::Matcher === matcher end def define_matcher(matcher, name, &block) diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 92f34e450..7a72ced59 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -90,6 +90,12 @@ module Mocks }.to raise_error(UnsupportedMatcherError) end + it 'does support inherited matchers', :unless => options.include?(:allow_other_matchers) do + receive_foo = Class.new(RSpec::Mocks::Matchers::Receive).new(:foo, nil) + wrapped.to receive_foo + receiver.foo + end + it 'does not get confused by messages being passed as strings and symbols' do wrapped.to receive(:foo).with(1) { :a } wrapped.to receive("foo").with(2) { :b } From 310ba5159a11465100fe6b7852f0664e327993bf Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 19 Jan 2016 10:00:21 +1100 Subject: [PATCH 0988/1179] cleanup and changelog for #1056 --- Changelog.md | 5 +++++ lib/rspec/mocks.rb | 5 ++++- lib/rspec/mocks/matchers/matcher.rb | 8 -------- 3 files changed, 9 insertions(+), 9 deletions(-) delete mode 100644 lib/rspec/mocks/matchers/matcher.rb diff --git a/Changelog.md b/Changelog.md index 9862b7d4e..ffc59150c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.1...master) +Bug Fixes: + +* Allow matchers which inherit from `rspec-mocks` matchers to be used for + `allow`. (Andrew Kozin, #1056) + ### 3.4.1 / 2016-01-10 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.0...v3.4.1) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 738a72df3..95ea1de4c 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -117,7 +117,10 @@ class << self # Namespace for mock-related matchers. module Matchers - autoload :Matcher, "rspec/mocks/matchers/matcher" + # @private + # just a "tag" for rspec-mock matchers detection + module Matcher; end + autoload :HaveReceived, "rspec/mocks/matchers/have_received" autoload :Receive, "rspec/mocks/matchers/receive" autoload :ReceiveMessageChain, "rspec/mocks/matchers/receive_message_chain" diff --git a/lib/rspec/mocks/matchers/matcher.rb b/lib/rspec/mocks/matchers/matcher.rb deleted file mode 100644 index 19414b870..000000000 --- a/lib/rspec/mocks/matchers/matcher.rb +++ /dev/null @@ -1,8 +0,0 @@ -module RSpec - module Mocks - module Matchers - # just a "tag" for rspec-mock matchers detection - module Matcher; end - end - end -end From 8e9dcdec2f2cc081a63c477b39d7edc4ca1ce985 Mon Sep 17 00:00:00 2001 From: Kilian Cirera Sant Date: Thu, 14 Jan 2016 14:59:55 +0100 Subject: [PATCH 0989/1179] Allow chaining and_yield for any_instance --- lib/rspec/mocks/any_instance/stub_chain.rb | 2 +- spec/rspec/mocks/any_instance_spec.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index adc4e6bb4..8e254bcbc 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -30,7 +30,7 @@ def invocation_order :with => [nil], :and_return => [:with, nil], :and_raise => [:with, nil], - :and_yield => [:with, nil], + :and_yield => [:with, :and_yield, nil], :and_call_original => [:with, nil], :and_wrap_original => [:with, nil] } diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index d5cb9b4cb..a3b6a7e27 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -33,6 +33,13 @@ def private_method; :private_method_return_value; end end let(:existing_method_return_value) { :existing_method_return_value } + context "chain" do + it "yields the value specified" do + allow_any_instance_of(klass).to receive(:foo).and_yield(1).and_yield(2) + expect { |b| klass.new.foo(&b) }.to yield_successive_args(1, 2) + end + end + context "invocation order" do context "when stubbing" do it "raises an error if 'with' follows 'and_return'" do @@ -46,6 +53,11 @@ def private_method; :private_method_return_value; end it "raises an error if 'with' follows 'and_yield'" do expect { allow_any_instance_of(klass).to receive(:foo).and_yield(1).with("1") }.to raise_error(NoMethodError) end + + it "allows chaining 'and_yield'" do + allow_any_instance_of(klass).to receive(:foo).and_yield(1).and_yield(2).and_yield(3) + expect { |b| klass.new.foo(&b) }.to yield_successive_args(1, 2, 3) + end end context "when setting a message expectation" do From 743d8b37bb8fbdce8433d88909d6977f820b64c9 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 19 Jan 2016 10:07:45 +1100 Subject: [PATCH 0990/1179] changelog for #1054 [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index ffc59150c..53a7ab95b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,8 @@ Bug Fixes: +* Allow `any_instance_of(...).to receive(...)` to use `and_yield` multiple + times. (Kilian Cirera Sant, #1054) * Allow matchers which inherit from `rspec-mocks` matchers to be used for `allow`. (Andrew Kozin, #1056) From 2c41466ed0e797ec5c164c0a9bc5a29afb681d11 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 20 Jan 2016 13:02:06 +1100 Subject: [PATCH 0991/1179] changelog for #1013 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 53a7ab95b..ab0d80b6f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,8 @@ Bug Fixes: times. (Kilian Cirera Sant, #1054) * Allow matchers which inherit from `rspec-mocks` matchers to be used for `allow`. (Andrew Kozin, #1056) +* Prevent stubbing `respond_to?` on partial doubles from causing infinite + recursion. (Jon Rowe, #1013) ### 3.4.1 / 2016-01-10 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.0...v3.4.1) From 2fcdbaee682efc35c146c9f713c079356ca3ec6e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 20 Jan 2016 13:26:11 +1100 Subject: [PATCH 0992/1179] fix spec for 1.8.7 (no idea why this wasn't caught earlier) --- spec/rspec/mocks/partial_double_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index c12236fa8..6de88ca45 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -54,8 +54,8 @@ def call(name) an_object = the_klass.new - expect(an_object).to receive(:respond_to?) - .with(:my_method).at_least(:once) { true } + expect(an_object).to receive(:respond_to?). + with(:my_method).at_least(:once) { true } expect(an_object).to receive(:my_method) an_object.call :my_method From f5f95f5b0368c05eb4beb7b9c5bcdcf6415aaf82 Mon Sep 17 00:00:00 2001 From: Joe Rafaniello Date: Thu, 4 Feb 2016 09:53:23 -0500 Subject: [PATCH 0993/1179] Restore 'owned' stubbed methods only in ruby 2.3+. Fixes #1057. In b94fe10e34ececd8cda34c480d996f036a58c557, we changed behavior to to restore 'owned' stubbed methods for all rubies. This doesn't work for all rubies because Method#owner for aliased methods is inconsistent [1]. Since, ruby 2.3+ broke our prior backup/restore mechanism, it's safer to change the original behavior for ruby 2.3+ only. A @klass can have methods implemented (see Method#owner) in @klass or inherited from a superclass. In ruby 2.2 and earlier, we can copy a method regardless of the 'owner' and restore it to @klass after because a call to 'super' from @klass's copied method would end up calling the original class's superclass's method. With the commit below [2], available starting in 2.3.0, ruby changed this behavior and a call to 'super' from the method copied to @klass will call @klass's superclass method, which is the original implementer of this method! This leads to very strange errors if @klass's copied method calls 'super', since it would end up calling itself, the original method implemented in @klass's superclass. For ruby 2.3 and above, we need to only restore methods that @klass originally owned. [1] https://gist.github.com/jrafanie/0e5b8a9f4e8b4f84913f [2] https://github.com/ruby/ruby/commit/c8854d2ca4be9ee6946e6d17b0e17d9ef130ee81 --- lib/rspec/mocks/any_instance/recorder.rb | 23 ++++++++++++++++++++++- spec/rspec/mocks/any_instance_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 8651e02ef..38fc16f23 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -193,7 +193,28 @@ def restore_original_method!(method_name) alias_method_name = build_alias_method_name(method_name) @klass.class_exec(@backed_up_method_owner) do |backed_up_method_owner| remove_method method_name - alias_method method_name, alias_method_name if backed_up_method_owner[method_name.to_sym] == self + + # A @klass can have methods implemented (see Method#owner) in @klass + # or inherited from a superclass. In ruby 2.2 and earlier, we can copy + # a method regardless of the 'owner' and restore it to @klass after + # because a call to 'super' from @klass's copied method would end up + # calling the original class's superclass's method. + # + # With the commit below, available starting in 2.3.0, ruby changed + # this behavior and a call to 'super' from the method copied to @klass + # will call @klass's superclass method, which is the original + # implementer of this method! This leads to very strange errors + # if @klass's copied method calls 'super', since it would end up + # calling itself, the original method implemented in @klass's + # superclass. + # + # For ruby 2.3 and above, we need to only restore methods that + # @klass originally owned. + # + # https://github.com/ruby/ruby/commit/c8854d2ca4be9ee6946e6d17b0e17d9ef130ee81 + if RUBY_VERSION < "2.3" || backed_up_method_owner[method_name.to_sym] == self + alias_method method_name, alias_method_name + end remove_method alias_method_name end end diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index a3b6a7e27..3cfa69e51 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -5,12 +5,22 @@ class GrandparentClass def foo(_a) 'bar' end + + def grandparent_method + 1 + end end class ParentClass < GrandparentClass def foo super(:a) end + + def caller_of_parent_aliased_method + parent_aliased_method + end + + alias parent_aliased_method grandparent_method end class ChildClass < ParentClass @@ -174,6 +184,17 @@ def private_method; :private_method_return_value; end end end + context 'aliased methods' do + it 'tracks aliased method calls' do + instance = AnyInstanceSpec::ParentClass.new + expect_any_instance_of(AnyInstanceSpec::ParentClass).to receive(:parent_aliased_method).with(no_args).and_return(2) + expect(instance.caller_of_parent_aliased_method).to eq 2 + + reset_all + expect(instance.caller_of_parent_aliased_method).to eq 1 + end + end + context "with argument matching" do before do allow_any_instance_of(klass).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) From c5ab20cc04892ce61c5f15def16ae8a6709bacca Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sat, 6 Feb 2016 14:22:32 +0000 Subject: [PATCH 0994/1179] Release RSpec Mocks v3.5.0.beta1 --- Changelog.md | 5 ++++- lib/rspec/mocks/version.rb | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index ab0d80b6f..705baacd4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.1...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta1...master) + +### 3.5.0.beta1 / 2016-02-06 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.1...v3.5.0.beta1) Bug Fixes: diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 00e9b7b3e..25f13a8f7 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.5.0.pre' + STRING = '3.5.0.beta1' end end end From 5981602b2f98b22faa699f7b28b75b88bf550abd Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sat, 6 Feb 2016 14:48:58 +0000 Subject: [PATCH 0995/1179] Return RSpec Mocks to version 3.5.0.pre --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 25f13a8f7..00e9b7b3e 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.5.0.beta1' + STRING = '3.5.0.pre' end end end From b3b698859503d77390c6cf21c26b0a1adb8f2bd3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 7 Feb 2016 11:40:13 +1100 Subject: [PATCH 0996/1179] changelog for #1060 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 705baacd4..3e47d186d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,8 @@ Bug Fixes: `allow`. (Andrew Kozin, #1056) * Prevent stubbing `respond_to?` on partial doubles from causing infinite recursion. (Jon Rowe, #1013) +* Prevent aliased methods from disapearing after being mocked with + `any_instance` (regression from #1043). (Joe Rafaniello, #1060) ### 3.4.1 / 2016-01-10 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.0...v3.4.1) From 591de6c2a85adc1d52d7afc643b03a1c4efaff25 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 7 Feb 2016 12:47:14 +1100 Subject: [PATCH 0997/1179] allow constraining arguments with receive_message_chain with `with` --- lib/rspec/mocks/any_instance/stub_chain_chain.rb | 1 + spec/rspec/mocks/any_instance_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/lib/rspec/mocks/any_instance/stub_chain_chain.rb b/lib/rspec/mocks/any_instance/stub_chain_chain.rb index c05696734..e5a23efe5 100644 --- a/lib/rspec/mocks/any_instance/stub_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain_chain.rb @@ -16,6 +16,7 @@ def create_message_expectation_on(instance) def invocation_order @invocation_order ||= { + :with => [nil], :and_return => [nil], :and_raise => [nil], :and_yield => [nil] diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 3cfa69e51..25cd5878b 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -112,6 +112,13 @@ def private_method; :private_method_return_value; end allow_any_instance_of(klass).to receive_message_chain('one.two.three').and_return(:four) expect(klass.new.one.two.three).to eq(:four) end + + it "can constrain the return value by the argument to the last call" do + allow_any_instance_of(klass).to receive_message_chain(:one, :plus).with(1) { 2 } + allow_any_instance_of(klass).to receive_message_chain(:one, :plus).with(2) { 3 } + expect(klass.new.one.plus(1)).to eq(2) + expect(klass.new.one.plus(2)).to eq(3) + end end end From 86e69908f3ed61c0c2c7a8ff934882310f2d9785 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 8 Feb 2016 10:13:42 +1100 Subject: [PATCH 0998/1179] changelog for #1061 [skip ci] --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3e47d186d..eee5f7d76 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta1...master) +Bug Fixes: + +* Fix issue that prevented `receive_message_chain(...).with(...)` working + correctly on "any instance" mocks. (Jon Rowe, #1061) + ### 3.5.0.beta1 / 2016-02-06 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.1...v3.5.0.beta1) From f5acf725aa1d68b94c1637b11ca91ee66590a32a Mon Sep 17 00:00:00 2001 From: Isaac Betesh Date: Mon, 29 Feb 2016 15:12:15 -0500 Subject: [PATCH 0999/1179] fixes #1062 -- make the error message when and_wrap_original is called on a pure double not refer to and_call_original --- lib/rspec/mocks/message_expectation.rb | 24 +++++++++++++--------- spec/rspec/mocks/and_wrap_original_spec.rb | 14 +++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 76cc414ee..dfc9bdb47 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -95,7 +95,7 @@ def and_return(first_value, *values) # counter.increment # expect(counter.count).to eq(original_count + 1) def and_call_original - and_wrap_original do |original, *args, &block| + wrap_original(__method__) do |original, *args, &block| original.call(*args, &block) end end @@ -113,15 +113,7 @@ def and_call_original # original_method.call(*args, &block).first(10) # end def and_wrap_original(&block) - if RSpec::Mocks::TestDouble === @method_double.object - @error_generator.raise_only_valid_on_a_partial_double(:and_call_original) - else - warn_about_stub_override if implementation.inner_action - @implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block) - @yield_receiver_to_implementation_block = false - end - - nil + wrap_original(__method__, &block) end # @overload and_raise @@ -601,6 +593,18 @@ def warn_about_stub_override "Called from #{CallerFilter.first_non_rspec_line}." ) end + + def wrap_original(method_name, &block) + if RSpec::Mocks::TestDouble === @method_double.object + @error_generator.raise_only_valid_on_a_partial_double(method_name) + else + warn_about_stub_override if implementation.inner_action + @implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block) + @yield_receiver_to_implementation_block = false + end + + nil + end end include ImplementationDetails diff --git a/spec/rspec/mocks/and_wrap_original_spec.rb b/spec/rspec/mocks/and_wrap_original_spec.rb index a69e3b4e9..21bacc25e 100644 --- a/spec/rspec/mocks/and_wrap_original_spec.rb +++ b/spec/rspec/mocks/and_wrap_original_spec.rb @@ -74,4 +74,18 @@ def results end end end + + context "on a pure test double" do + let(:instance) { double } + + it 'raises an error even if the double object responds to the message' do + expect(instance.to_s).to be_a(String) + mock_expectation = expect(instance).to receive(:to_s) + instance.to_s # to satisfy the expectation + + expect { + mock_expectation.and_wrap_original + }.to raise_error(/pure test double.*and_wrap_original.*partial double/i) + end + end end From 31baac3ace4ec0aff97d9e4e1928b06d18c685f8 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 1 Mar 2016 08:11:27 +1100 Subject: [PATCH 1000/1179] changelog and cleanup for #1063 --- Changelog.md | 5 +++++ spec/rspec/mocks/and_wrap_original_spec.rb | 14 ++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index eee5f7d76..a359e7e61 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta1...master) +Enhancements: + +* Improve error message displayed when using `and_wrap_original` on pure test + doubles. (betesh, #1063) + Bug Fixes: * Fix issue that prevented `receive_message_chain(...).with(...)` working diff --git a/spec/rspec/mocks/and_wrap_original_spec.rb b/spec/rspec/mocks/and_wrap_original_spec.rb index 21bacc25e..df9cae464 100644 --- a/spec/rspec/mocks/and_wrap_original_spec.rb +++ b/spec/rspec/mocks/and_wrap_original_spec.rb @@ -76,12 +76,18 @@ def results end context "on a pure test double" do - let(:instance) { double } + let(:instance) { double :my_method => :my_value } + + it 'raises an error' do + mock_expectation = allow(instance).to receive(:my_method) + + expect { + mock_expectation.and_wrap_original + }.to raise_error(/pure test double.*and_wrap_original.*partial double/i) + end it 'raises an error even if the double object responds to the message' do - expect(instance.to_s).to be_a(String) - mock_expectation = expect(instance).to receive(:to_s) - instance.to_s # to satisfy the expectation + mock_expectation = allow(instance).to receive(:inspect) expect { mock_expectation.and_wrap_original From ebe611f193e66acaec953a4b10f8fb00e3bf60b3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 4 Aug 2015 15:48:01 +1000 Subject: [PATCH 1001/1179] warn when ordered is used with `allow` --- Changelog.md | 2 ++ lib/rspec/mocks/message_expectation.rb | 11 +++++++++++ spec/rspec/mocks/block_return_value_spec.rb | 11 ++++++++++- spec/rspec/mocks/matchers/receive_spec.rb | 8 ++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index a359e7e61..bd3aed87f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Enhancements: * Improve error message displayed when using `and_wrap_original` on pure test doubles. (betesh, #1063) +* Issue warning when attempting to use unsupported + `allow(...).to receive(...).ordered`. (Jon Rowe, #1000) Bug Fixes: diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index dfc9bdb47..cbccf0dfb 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -328,6 +328,13 @@ def with(*args, &block) # expect(api).to receive(:run).ordered # expect(api).to receive(:finish).ordered def ordered(&block) + if type == :stub + RSpec.warning( + "`allow(...).to receive(..).ordered` is not supported and will" \ + "have no effect, use `and_return(*ordered_values)` instead." + ) + end + self.inner_implementation_action = block additional_expected_calls.times do @order_group.register(self) @@ -349,9 +356,13 @@ module ImplementationDetails attr_writer :expected_received_count, :expected_from, :argument_list_matcher protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation= + # @private + attr_reader :type + # rubocop:disable Style/ParameterLists def initialize(error_generator, expectation_ordering, expected_from, method_double, type=:expectation, opts={}, &implementation_block) + @type = type @error_generator = error_generator @error_generator.opts = opts @expected_from = expected_from diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 121fb2cc8..4f48a7970 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -48,7 +48,7 @@ end end - %w[once twice ordered].each do |method| + %w[once twice].each do |method| describe method do it "returns the value of executing the block" do obj = Object.new @@ -58,6 +58,15 @@ end end + describe 'ordered' do + it "returns the value of executing the block" do + obj = Object.new + expect_warning_with_call_site(__FILE__, __LINE__ + 1) + allow(obj).to receive(:foo).ordered { 'bar' } + expect(obj.foo).to eq('bar') + end + end + describe "times" do it "returns the value of executing the block" do obj = Object.new diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 7a72ced59..4285ceb5f 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -274,6 +274,14 @@ def receiver.method_missing(*); end # a poor man's stub... let(:target) { allow(object) } end + context 'ordered with receive counts' do + specify 'is not supported' do + a_dbl = double + expect_warning_with_call_site(__FILE__, __LINE__ + 1) + allow(a_dbl).to receive(:one).ordered + end + end + context 'on a class method, from a class with subclasses' do let(:superclass) { Class.new { def self.foo; "foo"; end }} let(:subclass_redef) { Class.new(superclass) { def self.foo; ".foo."; end }} From 43ea2ce751210fb3ccbe34771f08dc2d2b99e9c5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 17 Mar 2016 08:58:38 -0700 Subject: [PATCH 1002/1179] Add proper minitest integration. `require 'rspec/mocks/minitest_integration'` will configure minitest to work properly with rspec-mocks. This also deals with the addition of `expect` to Minitest::Spec 5.6+. Fixes #931. --- Changelog.md | 2 + features/.nav | 1 + .../outside_rspec/any_test_framework.feature | 116 +++++++++ features/outside_rspec/minitest.feature | 246 +++++++++++++----- lib/rspec/mocks/minitest_integration.rb | 68 +++++ lib/rspec/mocks/targets.rb | 69 +++-- spec/rspec/mocks_spec.rb | 3 + 7 files changed, 422 insertions(+), 83 deletions(-) create mode 100644 features/outside_rspec/any_test_framework.feature create mode 100644 lib/rspec/mocks/minitest_integration.rb diff --git a/Changelog.md b/Changelog.md index bd3aed87f..c5bf1d81b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,8 @@ Enhancements: doubles. (betesh, #1063) * Issue warning when attempting to use unsupported `allow(...).to receive(...).ordered`. (Jon Rowe, #1000) +* Add `rspec/mocks/minitest_integration`, to properly integration rspec-mocks + with minitest. (Myron Marston, #1065) Bug Fixes: diff --git a/features/.nav b/features/.nav index 4001274bd..c910a4276 100644 --- a/features/.nav +++ b/features/.nav @@ -40,5 +40,6 @@ - unstub.feature - outside_rspec: - minitest.feature + - any_test_framework.feature - standalone.feature - Changelog diff --git a/features/outside_rspec/any_test_framework.feature b/features/outside_rspec/any_test_framework.feature new file mode 100644 index 000000000..0d869782d --- /dev/null +++ b/features/outside_rspec/any_test_framework.feature @@ -0,0 +1,116 @@ +Feature: Integrate with any test framework + + rspec-mocks is a stand-alone gem that can be integrated with any test framework. The + example below demonstrates using rspec-mocks with [minitest](http://docs.seattlerb.org/minitest/), but these steps + would apply when integrating rspec-mocks with any library or framework: + + * Include `RSpec::Mocks::ExampleMethods` in your test context. This provides rspec-mocks' API. + * Call `RSpec::Mocks.setup` before a test begins. + * Call `RSpec::Mocks.verify` after a test completes to verify message expectations. Note + that this step is optional; rspec-core, for example, skips this when an example has already failed. + * Call `RSpec::Mocks.teardown` after a test completes (and after `verify`) to cleanup. This + _must_ be called, even if an error has occurred, so it generally goes in an `ensure` clause. + + Note: if you are using minitest, you'll probably want to use the built-in [minitest integration](./integrate-with-minitest). + + Scenario: Use rspec-mocks with Minitest + Given a file named "test/test_helper.rb" with: + """ruby + require 'minitest/autorun' + require 'rspec/mocks' + + module MinitestRSpecMocksIntegration + include ::RSpec::Mocks::ExampleMethods + + def before_setup + ::RSpec::Mocks.setup + super + end + + def after_teardown + super + ::RSpec::Mocks.verify + ensure + ::RSpec::Mocks.teardown + end + end + + Minitest::Test.send(:include, MinitestRSpecMocksIntegration) + """ + And a file named "test/rspec_mocks_test.rb" with: + """ruby + require 'test_helper' + + class RSpecMocksTest < Minitest::Test + def test_passing_positive_expectation + dbl = double + expect(dbl).to receive(:message) + dbl.message + end + + def test_failing_positive_expectation + dbl = double + expect(dbl).to receive(:message) + end + + def test_passing_negative_expectation + dbl = double + expect(dbl).to_not receive(:message) + end + + def test_failing_negative_expectation + dbl = double + expect(dbl).to_not receive(:message) + dbl.message + end + + def test_passing_positive_spy_expectation + bond = spy + bond.james + expect(bond).to have_received(:james) + end + + def test_failing_positive_spy_expectation + bond = spy + expect(bond).to have_received(:james) + end + + def test_passing_negative_spy_expectation + bond = spy + expect(bond).not_to have_received(:james) + end + + def test_failing_negative_spy_expectation + bond = spy + bond.james + expect(bond).not_to have_received(:james) + end + end + """ + When I run `ruby -Itest test/rspec_mocks_test.rb` + Then it should fail with the following output: + | 1) Error: | + | RSpecMocksTest#test_failing_negative_expectation: | + | RSpec::Mocks::MockExpectationError: (Double (anonymous)).message(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 2) Error: | + | RSpecMocksTest#test_failing_positive_expectation: | + | RSpec::Mocks::MockExpectationError: (Double (anonymous)).message(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 3) Error: | + | RSpecMocksTest#test_failing_positive_spy_expectation: | + | RSpec::Mocks::MockExpectationError: (Double (anonymous)).james(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 4) Error: | + | RSpecMocksTest#test_failing_negative_spy_expectation: | + | RSpec::Mocks::MockExpectationError: (Double (anonymous)).james(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 8 runs, 0 assertions, 0 failures, 4 errors, 0 skips | diff --git a/features/outside_rspec/minitest.feature b/features/outside_rspec/minitest.feature index b52c67047..586fe2c8e 100644 --- a/features/outside_rspec/minitest.feature +++ b/features/outside_rspec/minitest.feature @@ -1,43 +1,12 @@ Feature: Integrate with Minitest - rspec-mocks is a stand-alone gem that can be integrated with any test framework. The - example below demonstrates using rspec-mocks with [minitest](http://docs.seattlerb.org/minitest/), but these steps - would apply when integrating rspec-mocks with any library or framework: - - * Include `RSpec::Mocks::ExampleMethods` in your test context. This provides rspec-mocks' API. - * Call `RSpec::Mocks.setup` before a test begins. - * Call `RSpec::Mocks.verify` after a test completes to verify message expectations. Note - that this step is optional; rspec-core, for example, skips this when an example has already failed. - * Call `RSpec::Mocks.teardown` after a test completes (and after `verify`) to cleanup. This - _must_ be called, even if an error has occurred, so it generally goes in an `ensure` clause. - - Scenario: Use rspec-mocks with Minitest - Given a file named "test/test_helper.rb" with: - """ruby - require 'minitest/autorun' - require 'rspec/mocks' - - module MinitestRSpecMocksIntegration - include ::RSpec::Mocks::ExampleMethods + To use rspec-mocks with minitest, simply require `rspec/mocks/minitest_integration`. - def before_setup - ::RSpec::Mocks.setup - super - end - - def after_teardown - super - ::RSpec::Mocks.verify - ensure - ::RSpec::Mocks.teardown - end - end - - Minitest::Test.send(:include, MinitestRSpecMocksIntegration) - """ - And a file named "test/rspec_mocks_test.rb" with: + Scenario: Use rspec-mocks with Minitest::Test + Given a file named "test/rspec_mocks_test.rb" with: """ruby - require 'test_helper' + require 'minitest/autorun' + require 'rspec/mocks/minitest_integration' class RSpecMocksTest < Minitest::Test def test_passing_positive_expectation @@ -85,30 +54,183 @@ Feature: Integrate with Minitest end end """ - When I run `ruby -Itest test/rspec_mocks_test.rb` + When I run `ruby test/rspec_mocks_test.rb --seed 0` + Then it should fail with the following output: + | 1) Failure: | + | RSpecMocksTest#test_failing_positive_expectation | + | (Double (anonymous)).message(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 2) Failure: | + | RSpecMocksTest#test_failing_negative_expectation | + | (Double (anonymous)).message(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 3) Failure: | + | RSpecMocksTest#test_failing_positive_spy_expectation | + | (Double (anonymous)).james(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 4) Failure: | + | RSpecMocksTest#test_failing_negative_spy_expectation | + | (Double (anonymous)).james(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 8 runs, 0 assertions, 4 failures, 0 errors, 0 skips | + + Scenario: Use rspec-mocks with Minitest::Spec + Given a file named "spec/rspec_mocks_spec.rb" with: + """ruby + require 'minitest/autorun' + require 'minitest/spec' + require 'rspec/mocks/minitest_integration' + + describe "Minitest Spec integration" do + it 'passes a positive expectation' do + dbl = double + expect(dbl).to receive(:message) + dbl.message + end + + it 'fails a positive expectation' do + dbl = double + expect(dbl).to receive(:message) + end + + it 'passes a negative expectation (using to_not)' do + dbl = double + expect(dbl).to_not receive(:message) + end + + it 'fails a negative expectation (using not_to)' do + dbl = double + expect(dbl).not_to receive(:message) + dbl.message + end + end + """ + When I run `ruby spec/rspec_mocks_spec.rb --seed 0` + Then it should fail with the following output: + | 1) Failure: | + | Minitest Spec integration#test_0002_fails a positive expectation | + | (Double (anonymous)).message(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 2) Failure: | + | Minitest Spec integration#test_0004_fails a negative expectation (using not_to) | + | (Double (anonymous)).message(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 4 runs, 4 assertions, 2 failures, 0 errors, 0 skips | + + Scenario: Load rspec-mocks before rspec-expectations, with Minitest::Spec + Given a file named "spec/rspec_mocks_spec.rb" with: + """ruby + require 'minitest/autorun' + require 'minitest/spec' + require 'rspec/mocks/minitest_integration' + require 'rspec/expectations/minitest_integration' + + describe "Minitest Spec integration" do + it 'passes a positive expectation' do + dbl = double + expect(dbl).to receive(:message) + dbl.message + end + + it 'fails a positive expectation' do + dbl = double + expect(dbl).to receive(:message) + end + + it 'passes a negative expectation (using to_not)' do + dbl = double + expect(dbl).to_not receive(:message) + end + + it 'fails a negative expectation (using not_to)' do + dbl = double + expect(dbl).not_to receive(:message) + dbl.message + end + + it 'can use both minitest and rspec expectations' do + expect(1 + 3).must_equal 4 + expect(1 + 3).to eq 4 + end + end + """ + When I run `ruby spec/rspec_mocks_spec.rb --seed 0` + Then it should fail with the following output: + | 1) Failure: | + | Minitest Spec integration#test_0002_fails a positive expectation | + | (Double (anonymous)).message(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 2) Failure: | + | Minitest Spec integration#test_0004_fails a negative expectation (using not_to) | + | (Double (anonymous)).message(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 5 runs, 6 assertions, 2 failures, 0 errors, 0 skips | + + Scenario: Load rspec-mocks after rspec-expectations, with Minitest::Spec + Given a file named "spec/rspec_mocks_spec.rb" with: + """ruby + require 'minitest/autorun' + require 'minitest/spec' + require 'rspec/expectations/minitest_integration' + require 'rspec/mocks/minitest_integration' + + describe "Minitest Spec integration" do + it 'passes a positive expectation' do + dbl = double + expect(dbl).to receive(:message) + dbl.message + end + + it 'fails a positive expectation' do + dbl = double + expect(dbl).to receive(:message) + end + + it 'passes a negative expectation (using to_not)' do + dbl = double + expect(dbl).to_not receive(:message) + end + + it 'fails a negative expectation (using not_to)' do + dbl = double + expect(dbl).not_to receive(:message) + dbl.message + end + + it 'can use both minitest and rspec expectations' do + expect(1 + 3).must_equal 4 + expect(1 + 3).to eq 4 + end + end + """ + When I run `ruby spec/rspec_mocks_spec.rb --seed 0` Then it should fail with the following output: - | 1) Error: | - | RSpecMocksTest#test_failing_negative_expectation: | - | RSpec::Mocks::MockExpectationError: (Double (anonymous)).message(no args) | - | expected: 0 times with any arguments | - | received: 1 time | - | | - | 2) Error: | - | RSpecMocksTest#test_failing_positive_expectation: | - | RSpec::Mocks::MockExpectationError: (Double (anonymous)).message(*(any args)) | - | expected: 1 time with any arguments | - | received: 0 times with any arguments | - | | - | 3) Error: | - | RSpecMocksTest#test_failing_positive_spy_expectation: | - | RSpec::Mocks::MockExpectationError: (Double (anonymous)).james(*(any args)) | - | expected: 1 time with any arguments | - | received: 0 times with any arguments | - | | - | 4) Error: | - | RSpecMocksTest#test_failing_negative_spy_expectation: | - | RSpec::Mocks::MockExpectationError: (Double (anonymous)).james(no args) | - | expected: 0 times with any arguments | - | received: 1 time | - | | - | 8 runs, 0 assertions, 0 failures, 4 errors, 0 skips | + | 1) Failure: | + | Minitest Spec integration#test_0002_fails a positive expectation | + | (Double (anonymous)).message(*(any args)) | + | expected: 1 time with any arguments | + | received: 0 times with any arguments | + | | + | 2) Failure: | + | Minitest Spec integration#test_0004_fails a negative expectation (using not_to) | + | (Double (anonymous)).message(no args) | + | expected: 0 times with any arguments | + | received: 1 time | + | | + | 5 runs, 6 assertions, 2 failures, 0 errors, 0 skips | diff --git a/lib/rspec/mocks/minitest_integration.rb b/lib/rspec/mocks/minitest_integration.rb new file mode 100644 index 000000000..c7a7bab45 --- /dev/null +++ b/lib/rspec/mocks/minitest_integration.rb @@ -0,0 +1,68 @@ +require 'rspec/mocks' + +module RSpec + module Mocks + # @private + module MinitestIntegration + include ::RSpec::Mocks::ExampleMethods + + def before_setup + ::RSpec::Mocks.setup + super + end + + def after_teardown + super + + # Only verify if there's not already an error. Otherwise + # we risk getting the same failure twice, since negative + # expectation violations raise both when the message is + # unexpectedly received, and also during `verify` (in case + # the first failure was caught by user code via a + # `rescue Exception`). + ::RSpec::Mocks.verify unless failures.any? + ensure + ::RSpec::Mocks.teardown + end + end + end +end + +Minitest::Test.send(:include, RSpec::Mocks::MinitestIntegration) + +if defined?(::Minitest::Expectation) + if defined?(::RSpec::Expectations) && ::Minitest::Expectation.method_defined?(:to) + # rspec/expectations/minitest_integration has already been loaded and + # has defined `to`/`not_to`/`to_not` on `Minitest::Expectation` so we do + # not want to here (or else we would interfere with rspec-expectations' definition). + else + # ...otherwise, define those methods now. If `rspec/expectations/minitest_integration` + # is loaded after this file, it'll overide the defintion here. + Minitest::Expectation.class_eval do + include RSpec::Mocks::ExpectationTargetMethods + + def to(*args) + ctx.assertions += 1 + super + end + + def not_to(*args) + ctx.assertions += 1 + super + end + + def to_not(*args) + ctx.assertions += 1 + super + end + end + end +end + +module RSpec + module Mocks + remove_const :MockExpectationError + # Raised when a message expectation is not satisfied. + MockExpectationError = ::Minitest::Assertion + end +end diff --git a/lib/rspec/mocks/targets.rb b/lib/rspec/mocks/targets.rb index 61375b101..7130e895a 100644 --- a/lib/rspec/mocks/targets.rb +++ b/lib/rspec/mocks/targets.rb @@ -1,12 +1,8 @@ module RSpec module Mocks # @private - class TargetBase - def initialize(target) - @target = target - end - - def self.delegate_to(matcher_method) + module TargetDelegationClassMethods + def delegate_to(matcher_method) define_method(:to) do |matcher, &block| unless matcher_allowed?(matcher) raise_unsupported_matcher(:to, matcher) @@ -15,7 +11,7 @@ def self.delegate_to(matcher_method) end end - def self.delegate_not_to(matcher_method, options={}) + def delegate_not_to(matcher_method, options={}) method_name = options.fetch(:from) define_method(method_name) do |matcher, &block| case matcher @@ -29,11 +25,16 @@ def self.delegate_not_to(matcher_method, options={}) end end - def self.disallow_negation(method_name) + def disallow_negation(method_name) define_method(method_name) do |matcher, *_args| raise_negation_unsupported(method_name, matcher) end end + end + + # @private + module TargetDelegationInstanceMethods + attr_reader :target private @@ -42,7 +43,7 @@ def matcher_allowed?(matcher) end def define_matcher(matcher, name, &block) - matcher.__send__(name, @target, &block) + matcher.__send__(name, target, &block) end def raise_unsupported_matcher(method_name, matcher) @@ -56,31 +57,54 @@ def raise_negation_unsupported(method_name, matcher) "`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " \ "doesn't really make sense. What would it even mean?" end + end + + # @private + class TargetBase + def initialize(target) + @target = target + end + + extend TargetDelegationClassMethods + include TargetDelegationInstanceMethods + end + + # @private + module ExpectationTargetMethods + extend TargetDelegationClassMethods + include TargetDelegationInstanceMethods + + delegate_to :setup_expectation + delegate_not_to :setup_negative_expectation, :from => :not_to + delegate_not_to :setup_negative_expectation, :from => :to_not def expression - self.class::EXPRESSION + :expect end end + # @private + class ExpectationTarget < TargetBase + include ExpectationTargetMethods + end + # @private class AllowanceTarget < TargetBase - EXPRESSION = :allow + def expression + :allow + end + delegate_to :setup_allowance disallow_negation :not_to disallow_negation :to_not end - # @private - class ExpectationTarget < TargetBase - EXPRESSION = :expect - delegate_to :setup_expectation - delegate_not_to :setup_negative_expectation, :from => :not_to - delegate_not_to :setup_negative_expectation, :from => :to_not - end - # @private class AnyInstanceAllowanceTarget < TargetBase - EXPRESSION = :allow_any_instance_of + def expression + :allow_any_instance_of + end + delegate_to :setup_any_instance_allowance disallow_negation :not_to disallow_negation :to_not @@ -88,7 +112,10 @@ class AnyInstanceAllowanceTarget < TargetBase # @private class AnyInstanceExpectationTarget < TargetBase - EXPRESSION = :expect_any_instance_of + def expression + :expect_any_instance_of + end + delegate_to :setup_any_instance_expectation delegate_not_to :setup_any_instance_negative_expectation, :from => :not_to delegate_not_to :setup_any_instance_negative_expectation, :from => :to_not diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index ab8243e0f..d94ee9b12 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -2,6 +2,9 @@ RSpec.describe RSpec::Mocks do lib_preamble = [ + # We define minitest constants because rspec/mocks/minitest_integration + # expects these constants to already be defined. + "module Minitest; class Assertion; end; module Test; end; end", 'require "rspec/mocks"', # Must be required before other files due to how our autoloads are setup. # (Users won't hit this problem because they won't require all the files From b05f7331d58a664e5f8f613274a40d93c8c32609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20B=C3=BChlmann?= Date: Fri, 25 Mar 2016 11:02:59 +0100 Subject: [PATCH 1003/1179] Allow stubbing #throw on any_instance [#1067] --- lib/rspec/mocks/any_instance/stub_chain.rb | 1 + spec/rspec/mocks/any_instance_spec.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index 8e254bcbc..1e37576b0 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -31,6 +31,7 @@ def invocation_order :and_return => [:with, nil], :and_raise => [:with, nil], :and_yield => [:with, :and_yield, nil], + :and_throw => [:with, nil], :and_call_original => [:with, nil], :and_wrap_original => [:with, nil] } diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 25cd5878b..313b3e761 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -64,6 +64,10 @@ def private_method; :private_method_return_value; end expect { allow_any_instance_of(klass).to receive(:foo).and_yield(1).with("1") }.to raise_error(NoMethodError) end + it "raises an error if 'with' follows 'and_throw'" do + expect { allow_any_instance_of(klass).to receive(:foo).and_throw(:ball).with("football") }.to raise_error(NoMethodError) + end + it "allows chaining 'and_yield'" do allow_any_instance_of(klass).to receive(:foo).and_yield(1).and_yield(2).and_yield(3) expect { |b| klass.new.foo(&b) }.to yield_successive_args(1, 2, 3) @@ -309,6 +313,18 @@ def private_method; :private_method_return_value; end end end + context "with #and_throw" do + it "can stub a method that doesn't exist" do + allow_any_instance_of(klass).to receive(:foo).and_throw(:up) + expect { klass.new.foo }.to throw_symbol(:up) + end + + it "can stub a method that exists" do + allow_any_instance_of(klass).to receive(:existing_method).and_throw(:up) + expect { klass.new.existing_method }.to throw_symbol(:up) + end + end + context "with a block" do it "stubs a method" do allow_any_instance_of(klass).to receive(:foo) { 1 } From bc2fb8c95124b68898f4a29d6d0777043daf3016 Mon Sep 17 00:00:00 2001 From: Benjamin Quorning Date: Tue, 29 Mar 2016 11:11:34 +0200 Subject: [PATCH 1004/1179] Link to homepage using https --- rspec-mocks.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 685418f5a..0e9cdc0eb 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.license = "MIT" s.authors = ["Steven Baker", "David Chelimsky", "Myron Marston"] s.email = "rspec@googlegroups.com" - s.homepage = "http://github.com/rspec/rspec-mocks" + s.homepage = "https://github.com/rspec/rspec-mocks" s.summary = "rspec-mocks-#{RSpec::Mocks::Version::STRING}" s.description = "RSpec's 'test double' framework, with support for stubbing and mocking" From 84d13c81dead79592b2d4e170d75043568019825 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sat, 2 Apr 2016 10:58:30 -0400 Subject: [PATCH 1005/1179] Temporarily set version for release of 3.5.0.beta3 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 00e9b7b3e..a7aa7a366 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.5.0.pre' + STRING = '3.5.0.beta3' end end end From cbea99930533eb0346bd93352a24de0e22d5a138 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sat, 2 Apr 2016 11:22:30 -0400 Subject: [PATCH 1006/1179] Revert "Temporarily set version for release of 3.5.0.beta3" This reverts commit 84d13c81dead79592b2d4e170d75043568019825. --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index a7aa7a366..00e9b7b3e 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.5.0.beta3' + STRING = '3.5.0.pre' end end end From f1e6d1053d35dc933796e6df4528e4726478fea8 Mon Sep 17 00:00:00 2001 From: Ryan Beckman Date: Fri, 8 Apr 2016 00:55:05 -0700 Subject: [PATCH 1007/1179] Change typo "contraint" to "constraint" in have_received_spec. --- spec/rspec/mocks/matchers/have_received_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index a75bcd95a..797ecdb09 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -229,7 +229,7 @@ module Mocks end end - context "when contrained with grouped arguments `with`" do + context "when constrained with grouped arguments `with`" do it 'groups the "got" arguments based on the method call that included them' do dbl = double(:expected_method => nil) dbl.expected_method(:one, :four) From 2136910eeac00a231c5def873272219d188168df Mon Sep 17 00:00:00 2001 From: Martin Emde Date: Sun, 10 Apr 2016 19:44:18 -0700 Subject: [PATCH 1008/1179] Correct allow_expectations_on_nil error message The error message suggested that you set the wrong config. `config.allow_expectations_on_nil` instead of `config.allow_message_expectations_on_nil` --- lib/rspec/mocks/error_generator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 3ba04c6c0..94ddc7d16 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -214,8 +214,8 @@ def raise_expectation_on_nil_error(method_name) def expectation_on_nil_message(method_name) "An expectation of `:#{method_name}` was set on `nil`. " \ - "To allow expectations on `nil` and suppress this message, set `config.allow_expectations_on_nil` to `true`. " \ - "To disallow expectations on `nil`, set `config.allow_expectations_on_nil` to `false`" + "To allow expectations on `nil` and suppress this message, set `config.allow_message_expectations_on_nil` to `true`. " \ + "To disallow expectations on `nil`, set `config.allow_message_expectations_on_nil` to `false`" end private From e523c5e4fbd1639dfc7881d1a16d15ff458fc486 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 20 Apr 2016 21:43:33 +1000 Subject: [PATCH 1009/1179] changelog for #1067 --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index c5bf1d81b..cf3c07e80 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Enhancements: `allow(...).to receive(...).ordered`. (Jon Rowe, #1000) * Add `rspec/mocks/minitest_integration`, to properly integration rspec-mocks with minitest. (Myron Marston, #1065) +* Add `and_throw` to any instance handling. (Tobias Bühlmann, #1068) Bug Fixes: From 660f1e07660fd4daf0d15c9a9dfafa3e50856ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 29 Apr 2016 08:34:35 +0200 Subject: [PATCH 1010/1179] Fix incorrect JRUBY version check Previous check would also match JRuby 9000 versions. --- lib/rspec/mocks/method_reference.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 844ee71a2..423c1ff11 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -1,3 +1,5 @@ +RSpec::Support.require_rspec_support 'comparable_version' + module RSpec module Mocks # Represents a method on an object that may or may not be defined. @@ -128,7 +130,7 @@ def method_implemented?(mod) # # This is necessary due to a bug in JRuby prior to 1.7.5 fixed in: # https://github.com/jruby/jruby/commit/99a0613fe29935150d76a9a1ee4cf2b4f63f4a27 - if RUBY_PLATFORM == 'java' && JRUBY_VERSION.split('.')[-1].to_i < 5 + if RUBY_PLATFORM == 'java' && RSpec::Support::ComparableVersion.new(JRUBY_VERSION) < '1.7.5' def find_method(mod) mod.dup.instance_method(@method_name) end From e2a360197365a03f737724ee75726d2d0bc72ad7 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 30 Apr 2016 11:57:31 +1000 Subject: [PATCH 1011/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 4 ++-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 53d476e33..eba30f2e9 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. +# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 811457aef..fd3b8100a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. +# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index eaaba1c15..cc506c46b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. +# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -18,7 +18,7 @@ install: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - ruby --version - gem --version - - gem install bundler + - gem install bundler -v 1.11.2 # pin 1.11.2 version until later versions work - bundler --version - bundle install - cinst ansicon diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 706eea4de..ab0d99519 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. +# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 36f5f094c..bc2e51098 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. +# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index e3149cd17..b2884b43f 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. +# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index f5303e14e..2524c849f 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. +# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index f1f8704a0..ec137093c 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-01-06T22:57:44+11:00 from the rspec-dev repo. +# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 2d85bc9dfb93f57611c2210b828ba55e8e3f5514 Mon Sep 17 00:00:00 2001 From: Marc Ignacio Date: Wed, 4 May 2016 21:35:08 +0800 Subject: [PATCH 1012/1179] Add explicit link to Configuring Responses section for `*_any_instance_of` --- features/working_with_legacy_code/any_instance.feature | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/working_with_legacy_code/any_instance.feature b/features/working_with_legacy_code/any_instance.feature index 757af13e0..244f0d112 100644 --- a/features/working_with_legacy_code/any_instance.feature +++ b/features/working_with_legacy_code/any_instance.feature @@ -11,6 +11,8 @@ Feature: Any Instance These methods add the appropriate stub or expectation to all instances of `Widget`. + You can also [configure the responses](../configuring-responses) in the same manner. + This feature is sometimes useful when working with legacy code, though in general we discourage its use for a number of reasons: From 78c27640a8a9730c79da6be9863f8f15b8d0e72a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 6 May 2016 13:38:57 +1000 Subject: [PATCH 1013/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 6 +++--- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index eba30f2e9..21255969a 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index fd3b8100a..586457ac7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -9,7 +9,7 @@ cache: before_install: - "script/clone_all_rspec_repos" # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it - - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler; fi + - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler --version "!= 1.12.0, != 1.12.1"; fi bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: @@ -19,7 +19,7 @@ rvm: - 2.0.0 - 2.1 - 2.2 - - 2.3.0 + - 2.3.1 - ruby-head - ree - rbx diff --git a/appveyor.yml b/appveyor.yml index cc506c46b..ea1b8e6fc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index ab0d99519..e1cf51e52 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index bc2e51098..7b59c5c0e 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index b2884b43f..a0720bbef 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 2524c849f..5b4cb37b3 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index ec137093c..4b0d284c8 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-04-30T11:57:31+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 74faa53365bb8a2f771681bb2e8b49924d217cfe Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 9 May 2016 22:13:18 +1000 Subject: [PATCH 1014/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 7 ++++--- appveyor.yml | 4 ++-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 21255969a..1b7267610 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 586457ac7..aea9fe057 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -8,8 +8,9 @@ cache: - ../bundle before_install: - "script/clone_all_rspec_repos" - # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it - - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler --version "!= 1.12.0, != 1.12.1"; fi + # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it, the excluded versions are broken on Ruby 2.3 + - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler --version "!= 1.12.0, != 1.12.1, != 1.12.2, != 1.12.3"; fi +install: if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then bundle _1.11.2_ install --binstubs --standalone --without documentation --path ../bundle; else bundle install --binstubs --standalone --without documentation --path ../bundle; fi bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: diff --git a/appveyor.yml b/appveyor.yml index ea1b8e6fc..743389358 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -18,7 +18,7 @@ install: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - ruby --version - gem --version - - gem install bundler -v 1.11.2 # pin 1.11.2 version until later versions work + - gem install bundler -v "!= 1.12.0, != 1.12.1, != 1.12.2, != 1.12.3" - bundler --version - bundle install - cinst ansicon diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e1cf51e52..696873847 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 7b59c5c0e..9dbcff1b0 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index a0720bbef..9ebc2fa3f 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 5b4cb37b3..6a1579053 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 4b0d284c8..19530edef 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-06T13:38:57+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From bf5e8575ec29c7134b821d233e0757eba25627db Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 11 May 2016 21:32:02 +1000 Subject: [PATCH 1015/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 6 +++--- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 1b7267610..59233df0e 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index aea9fe057..8829779a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -9,8 +9,8 @@ cache: before_install: - "script/clone_all_rspec_repos" # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it, the excluded versions are broken on Ruby 2.3 - - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler --version "!= 1.12.0, != 1.12.1, != 1.12.2, != 1.12.3"; fi -install: if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then bundle _1.11.2_ install --binstubs --standalone --without documentation --path ../bundle; else bundle install --binstubs --standalone --without documentation --path ../bundle; fi + - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler --version "1.11.2"; fi + - alias bundle="bundle _1.11.2_" bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: diff --git a/appveyor.yml b/appveyor.yml index 743389358..16cd1624d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 696873847..7cd818fab 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 9dbcff1b0..ba69cb761 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 9ebc2fa3f..ae1d724a9 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 6a1579053..a92ef8e02 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 19530edef..7b1178be6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-09T22:13:18+10:00 from the rspec-dev repo. +# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From a25774f2b54ab199c355ff92afff75513e773531 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 31 May 2016 10:51:29 -0700 Subject: [PATCH 1016/1179] Update changelog to reflect 3.5.0 beta releases. --- Changelog.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index cf3c07e80..cc84fb1c8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,15 +1,27 @@ ### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta1...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta3...master) + +Enhancements: + +* Add `and_throw` to any instance handling. (Tobias Bühlmann, #1068) + +### 3.5.0.beta3 / 2016-04-02 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta2...v3.5.0.beta3) Enhancements: -* Improve error message displayed when using `and_wrap_original` on pure test - doubles. (betesh, #1063) * Issue warning when attempting to use unsupported `allow(...).to receive(...).ordered`. (Jon Rowe, #1000) -* Add `rspec/mocks/minitest_integration`, to properly integration rspec-mocks +* Add `rspec/mocks/minitest_integration`, to properly integrate rspec-mocks with minitest. (Myron Marston, #1065) -* Add `and_throw` to any instance handling. (Tobias Bühlmann, #1068) + +### 3.5.0.beta2 / 2016-03-10 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta1...v3.5.0.beta2) + +Enhancements: + +* Improve error message displayed when using `and_wrap_original` on pure test + doubles. (betesh, #1063) Bug Fixes: From 8a99189eba371108e1e8e9aa95d48507ce339f6f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 5 Jun 2016 10:50:52 -0700 Subject: [PATCH 1017/1179] Release v3.5.0.beta4 --- Changelog.md | 7 +++++-- lib/rspec/mocks/version.rb | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index cc84fb1c8..acbf15f5d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ -### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta3...master) +### 3.5 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta4...master) + +### 3.5.0.beta4 / 2016-06-05 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta3...v3.5.0.beta4) Enhancements: diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 00e9b7b3e..4a7ec9ce6 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.5.0.pre' + STRING = '3.5.0.beta4' end end end From ee3abc060ae95462d775e058687910509b8d7288 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 5 Jun 2016 10:58:41 -0700 Subject: [PATCH 1018/1179] Revert version to 3.5.0.pre --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 4a7ec9ce6..00e9b7b3e 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.5.0.beta4' + STRING = '3.5.0.pre' end end end From 334f2d6307fddab5a9e164bdf959cedd0324329b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 5 Jun 2016 11:48:42 -0700 Subject: [PATCH 1019/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 ++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 59233df0e..6b487c1d6 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. +# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 8829779a0..f305c91a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,9 @@ -# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. +# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby sudo: false +email: false cache: directories: - ../bundle diff --git a/appveyor.yml b/appveyor.yml index 16cd1624d..e3e93844e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. +# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 7cd818fab..068014f2c 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. +# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index ba69cb761..885f185be 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. +# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index ae1d724a9..95feb9e52 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. +# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index a92ef8e02..5fd1d5046 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. +# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 7b1178be6..69514fbda 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-05-11T21:32:01+10:00 from the rspec-dev repo. +# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 99bc76899a64863a05a0989c240e5238b832c217 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 14 Jun 2016 22:22:14 -0700 Subject: [PATCH 1020/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 4 ++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 6b487c1d6..712141c35 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. +# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index f305c91a2..7b013d790 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. +# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -20,7 +20,7 @@ rvm: - 1.9.3 - 2.0.0 - 2.1 - - 2.2 + - 2.2.5 - 2.3.1 - ruby-head - ree diff --git a/appveyor.yml b/appveyor.yml index e3e93844e..44f17544f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. +# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 068014f2c..052d37f0c 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. +# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 885f185be..809d71e56 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. +# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 95feb9e52..08fb02458 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. +# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 5fd1d5046..6c5c48bc7 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. +# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 69514fbda..f2d2bc802 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-05T11:48:42-07:00 from the rspec-dev repo. +# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 915b15bc8f1fe43002aa2fbf5e8c2c842ea5052f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 15 Jun 2016 10:57:34 -0700 Subject: [PATCH 1021/1179] Provide a nice string representation of a message expectation. --- Changelog.md | 5 ++ lib/rspec/mocks/error_generator.rb | 52 ++++++++++--------- lib/rspec/mocks/message_expectation.rb | 8 +++ ..._expectation_string_representation_spec.rb | 36 +++++++++++++ 4 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 spec/rspec/mocks/message_expectation_string_representation_spec.rb diff --git a/Changelog.md b/Changelog.md index acbf15f5d..5052c3ddb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### 3.5 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta4...master) +Enhancements: + +* Provides a nice string representation of + `RSpec::Mocks::MessageExpectation` (Myron Marston, #1095) + ### 3.5.0.beta4 / 2016-06-05 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta3...v3.5.0.beta4) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 94ddc7d16..eb47aea3b 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -218,6 +218,33 @@ def expectation_on_nil_message(method_name) "To disallow expectations on `nil`, set `config.allow_message_expectations_on_nil` to `false`" end + # @private + def intro(unwrapped=false) + case @target + when TestDouble then TestDoubleFormatter.format(@target, unwrapped) + when Class then + formatted = "#{@target.inspect} (class)" + return formatted if unwrapped + "#<#{formatted}>" + when NilClass then "nil" + else @target.inspect + end + end + + # @private + def method_call_args_description(args, generic_prefix=" with arguments: ", matcher_prefix=" with ") + case args.first + when ArgumentMatchers::AnyArgsMatcher then "#{matcher_prefix}any arguments" + when ArgumentMatchers::NoArgsMatcher then "#{matcher_prefix}no arguments" + else + if yield + "#{generic_prefix}#{format_args(args)}" + else + "" + end + end + end + private def received_part_of_expectation_error(actual_received_count, args) @@ -234,19 +261,6 @@ def expected_part_of_expectation_error(expected_received_count, expectation_coun end end - def method_call_args_description(args) - case args.first - when ArgumentMatchers::AnyArgsMatcher then " with any arguments" - when ArgumentMatchers::NoArgsMatcher then " with no arguments" - else - if yield - " with arguments: #{format_args(args)}" - else - "" - end - end - end - def unexpected_arguments_message(expected_args_string, actual_args_string) "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}" end @@ -290,18 +304,6 @@ def differ RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?) end - def intro(unwrapped=false) - case @target - when TestDouble then TestDoubleFormatter.format(@target, unwrapped) - when Class then - formatted = "#{@target.inspect} (class)" - return formatted if unwrapped - "#<#{formatted}>" - when NilClass then "nil" - else @target - end - end - def __raise(message, backtrace_line=nil, source_id=nil) message = opts[:message] unless opts[:message].nil? exception = RSpec::Mocks::MockExpectationError.new(message) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index cbccf0dfb..c824e4a4f 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -343,6 +343,14 @@ def ordered(&block) self end + # @return [String] a nice representation of the message expectation + def to_s + args_description = error_generator.method_call_args_description(@argument_list_matcher.expected_args, "", "") { true } + args_description = "(#{args_description})" unless args_description.start_with?("(") + "#<#{self.class} #{error_generator.intro}.#{message}#{args_description}>" + end + alias inspect to_s + # @private # Contains the parts of `MessageExpectation` that aren't part of # rspec-mocks' public API. The class is very big and could really use diff --git a/spec/rspec/mocks/message_expectation_string_representation_spec.rb b/spec/rspec/mocks/message_expectation_string_representation_spec.rb new file mode 100644 index 000000000..0a3bcafe1 --- /dev/null +++ b/spec/rspec/mocks/message_expectation_string_representation_spec.rb @@ -0,0 +1,36 @@ +module RSpec + module Mocks + RSpec.describe MessageExpectation, "has a nice string representation" do + let(:test_double) { double } + + example "for a raw message expectation on a test double" do + expect(allow(test_double).to receive(:foo)).to have_string_representation( + "#.foo(any arguments)>" + ) + end + + example "for a raw message expectation on a partial double" do + expect(allow("partial double").to receive(:foo)).to have_string_representation( + '#' + ) + end + + example "for a message expectation constrained by `with`" do + expect(allow(test_double).to receive(:foo).with(1, a_kind_of(String), any_args)).to have_string_representation( + "#.foo(1, a kind of String, *(any args))>" + ) + end + + RSpec::Matchers.define :have_string_representation do |expected_representation| + match do |object| + values_match?(expected_representation, object.to_s) && object.to_s == object.inspect + end + + failure_message do |object| + "expected string representation: #{expected_representation}\n" \ + " but got string representation: #{object.to_s}" + end + end + end + end +end From 8b11da4f651ab9b830129b2bc8393d40c3012264 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 15 Jun 2016 11:30:58 -0700 Subject: [PATCH 1022/1179] Add temporary work around for 1.8.7 warning. --- ...e_expectation_string_representation_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/rspec/mocks/message_expectation_string_representation_spec.rb b/spec/rspec/mocks/message_expectation_string_representation_spec.rb index 0a3bcafe1..ffe2c3c9f 100644 --- a/spec/rspec/mocks/message_expectation_string_representation_spec.rb +++ b/spec/rspec/mocks/message_expectation_string_representation_spec.rb @@ -1,3 +1,21 @@ +# Add temporary monkey patch to see if this fixes the 1.8.7 travis build +# before we merge the rspec-expectations PR with this fix. +RSpec::Matchers::EnglishPhrasing.class_eval do + if RUBY_VERSION == '1.8.7' + # Not sure why, but on travis on 1.8.7 we have gotten these warnings: + # lib/rspec/matchers/english_phrasing.rb:28: warning: default `to_a' will be obsolete + # So it appears that `Array` can trigger that (e.g. by calling `to_a` on the passed object?) + # So here we replace `Kernel#Array` with our own warning-free implementation for 1.8.7. + # @private + def self.Array(obj) + case obj + when Array then obj + else [obj] + end + end + end +end + module RSpec module Mocks RSpec.describe MessageExpectation, "has a nice string representation" do From 53fd0a3daaa2b659aba18b0fc1dcdc8e698bb82f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 15 Jun 2016 15:57:14 -0700 Subject: [PATCH 1023/1179] Revert "Add temporary work around for 1.8.7 warning." This reverts commit 8b11da4f651ab9b830129b2bc8393d40c3012264. --- ...e_expectation_string_representation_spec.rb | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/spec/rspec/mocks/message_expectation_string_representation_spec.rb b/spec/rspec/mocks/message_expectation_string_representation_spec.rb index ffe2c3c9f..0a3bcafe1 100644 --- a/spec/rspec/mocks/message_expectation_string_representation_spec.rb +++ b/spec/rspec/mocks/message_expectation_string_representation_spec.rb @@ -1,21 +1,3 @@ -# Add temporary monkey patch to see if this fixes the 1.8.7 travis build -# before we merge the rspec-expectations PR with this fix. -RSpec::Matchers::EnglishPhrasing.class_eval do - if RUBY_VERSION == '1.8.7' - # Not sure why, but on travis on 1.8.7 we have gotten these warnings: - # lib/rspec/matchers/english_phrasing.rb:28: warning: default `to_a' will be obsolete - # So it appears that `Array` can trigger that (e.g. by calling `to_a` on the passed object?) - # So here we replace `Kernel#Array` with our own warning-free implementation for 1.8.7. - # @private - def self.Array(obj) - case obj - when Array then obj - else [obj] - end - end - end -end - module RSpec module Mocks RSpec.describe MessageExpectation, "has a nice string representation" do From c1893df09660561a1b4bb6b9a43ac5abc4838c36 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 1 Jul 2016 09:24:25 +0100 Subject: [PATCH 1024/1179] Bump version to 3.5.0 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 00e9b7b3e..87f01e289 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.5.0.pre' + STRING = '3.5.0' end end end From 48f8dac4ca63db999270d06a37a313c8ab26e1c5 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 1 Jul 2016 16:25:31 +0100 Subject: [PATCH 1025/1179] Add changelog entry for v3.5.0 --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5052c3ddb..dbbaff036 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.5 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta4...master) +### 3.5.0 / 2016-07-01 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta4...v3.5.0) Enhancements: From 4baf33cdacb8ea9d812bbd5cef1114c7e7a2e7fe Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Fri, 1 Jul 2016 17:17:44 +0100 Subject: [PATCH 1026/1179] Bump version to 3.6.0.pre --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 87f01e289..259c006d8 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.5.0' + STRING = '3.6.0.pre' end end end From 9a7f055bbde60455997db70d60e9f5a68f1d680d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 14 Jul 2016 09:50:14 +1000 Subject: [PATCH 1027/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 6 +++++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 712141c35..82a8b7756 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. +# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 7b013d790..0fccf5c70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. +# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -22,6 +22,7 @@ rvm: - 2.1 - 2.2.5 - 2.3.1 + - 2.4.0-preview1 - ruby-head - ree - rbx @@ -35,11 +36,14 @@ matrix: env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - rvm: jruby env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' + - rvm: jruby-9.0.3.0 + env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' allow_failures: - rvm: jruby-head env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - rvm: ruby-head - rvm: rbx + - rvm: 2.4.0-preview1 fast_finish: true branches: only: diff --git a/appveyor.yml b/appveyor.yml index 44f17544f..db29971a5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. +# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 052d37f0c..529133c46 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. +# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 809d71e56..297098dbf 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. +# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 08fb02458..97605e323 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. +# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 6c5c48bc7..a544c54f6 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. +# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index f2d2bc802..98713e234 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-06-14T22:22:14-07:00 from the rspec-dev repo. +# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 378fa486dcc7f2b06738711a219a17aa3bf197cf Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 14 Jul 2016 11:53:24 +1000 Subject: [PATCH 1028/1179] manually force json below 2.0.0 on older platforms --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index fcccdb837..91231b0f7 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,10 @@ end gem 'simplecov', '~> 0.8' +if RUBY_VERSION < '2.0.0' || RUBY_ENGINE == 'java' + gem 'json', '< 2.0.0' +end + platforms :jruby do gem "jruby-openssl" end From b3c90646fbca3b5201b22d0c45adc06bf9da620e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 16 Jul 2016 19:23:21 +1000 Subject: [PATCH 1029/1179] update jruby version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0fccf5c70..42b8802b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ matrix: env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - rvm: jruby env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - - rvm: jruby-9.0.3.0 + - rvm: jruby-9.1.2.0 env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' allow_failures: - rvm: jruby-head From 0ef423269a01d1777bb665f485b0b085023ea773 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 18 Jul 2016 09:20:59 +1000 Subject: [PATCH 1030/1179] pend broken spec on JRuby 9000 --- spec/rspec/mocks_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index d94ee9b12..fd91a02e4 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -132,7 +132,7 @@ expect(defined?(ValueY)).to be_falsey end - it 'does not allow the stubbed constants to be used after the scope in before(:all)' do + it 'does not allow the stubbed constants to be used after the scope in before(:all)', :pending => RSpec::Support::Ruby.jruby_9000? do expect(@error).to be_a(NameError) expect(@error.message).to include("ValueX") end From e77b27a99932c0ea004b2c9b3b39f31b787279a4 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 19 Jul 2016 09:21:23 +1000 Subject: [PATCH 1031/1179] comment about json [skip ci] --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 91231b0f7..65154a497 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ end gem 'simplecov', '~> 0.8' if RUBY_VERSION < '2.0.0' || RUBY_ENGINE == 'java' - gem 'json', '< 2.0.0' + gem 'json', '< 2.0.0' # this is a dependency of simplecov end platforms :jruby do From 96e6b0566b7015aa047ccdf193b06b1ec6c774e6 Mon Sep 17 00:00:00 2001 From: Alessandro Berardi Date: Fri, 5 Aug 2016 16:30:24 +0930 Subject: [PATCH 1032/1179] Return the instance of the test double from `double.freeze` --- lib/rspec/mocks/test_double.rb | 1 + spec/rspec/mocks/test_double_spec.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 3321ce1dc..54ffc92ed 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -66,6 +66,7 @@ def __disallow_further_usage! # doubles. def freeze RSpec.warn_with("WARNING: you attempted to freeze a test double. This is explicitly a no-op as freezing doubles can lead to undesired behaviour when resetting tests.") + self end private diff --git a/spec/rspec/mocks/test_double_spec.rb b/spec/rspec/mocks/test_double_spec.rb index 945066091..51c713642 100644 --- a/spec/rspec/mocks/test_double_spec.rb +++ b/spec/rspec/mocks/test_double_spec.rb @@ -23,6 +23,11 @@ module Mocks subject.hi }.not_to raise_error end + + it "returns the instance of the test double" do + allow(RSpec).to receive(:warn_with).with(/freeze a test double/) + expect(subject.freeze).to eq subject + end end RSpec.shared_examples_for "a copy method" do |method| From fda5a47c60d92b8874aa816e3245102fb21ef25a Mon Sep 17 00:00:00 2001 From: Alessandro Berardi Date: Fri, 5 Aug 2016 16:30:32 +0930 Subject: [PATCH 1033/1179] Adds changelog entry for #1109 --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index dbbaff036..0aebb5a42 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0...master) + +Bug Fixes: + +* Return the instance of the test double from + `double.freeze` (Alessandro Berardi, #1109) + ### 3.5.0 / 2016-07-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta4...v3.5.0) From 097bf1f54d1082b4c8e2d7e421614f4d2e252641 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 5 Aug 2016 17:34:29 +1000 Subject: [PATCH 1034/1179] Cleanup for #1110 --- Changelog.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0aebb5a42..29759f5f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,8 +3,7 @@ Bug Fixes: -* Return the instance of the test double from - `double.freeze` (Alessandro Berardi, #1109) +* Return the test double instance form `#freeze` (Alessandro Berardi, #1109) ### 3.5.0 / 2016-07-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta4...v3.5.0) From 150bc232f7f2917276d4503529adda8621c65eba Mon Sep 17 00:00:00 2001 From: mrageh Date: Thu, 25 Aug 2016 18:38:50 +0100 Subject: [PATCH 1035/1179] Remove legacy add_negative_message_expectation method In the past this method was used to negate expectations, but since [1] it's not been used. Therefore this commit removes it. 1.) https://github.com/rspec/rspec-mocks/commit/9357e4de76838157c8ea7d4a4ad6d0ae11882b22 --- lib/rspec/mocks/proxy.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 5e66a592c..d9e6ee240 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -439,11 +439,6 @@ def add_message_expectation(method_name, opts={}, &block) super end - def add_negative_message_expectation(location, method_name, &implementation) - warn_or_raise!(method_name) - super - end - def add_stub(method_name, opts={}, &implementation) warn_or_raise!(method_name) super From 3f14877d41cf42ea25ca14f6e3358c543b1c59a0 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 12 Sep 2016 11:51:17 +1000 Subject: [PATCH 1036/1179] switch git from git to https --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 65154a497..ff04cffa4 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp if File.exist?(library_path) && !ENV['USE_GIT_REPOS'] gem lib, :path => library_path else - gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => branch + gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => branch end end From 569626e364328e3919f0cfd9b22f1422e1ffb679 Mon Sep 17 00:00:00 2001 From: proby Date: Mon, 26 Sep 2016 16:17:58 -0700 Subject: [PATCH 1037/1179] use the RSpec::Support.method_handle_for instead of Class#method (#1119) --- lib/rspec/mocks/method_reference.rb | 2 +- spec/rspec/mocks/partial_double_spec.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index 423c1ff11..fa08e3704 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -189,7 +189,7 @@ def self.applies_to?(method_name) # We only want to apply our special logic to normal `new` methods. # Methods that the user has monkeyed with should be left as-is. - klass.method(:new).owner == ::Class + ::RSpec::Support.method_handle_for(klass, :new).owner == ::Class end def with_signature diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 6de88ca45..60c0a961b 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -566,6 +566,16 @@ class << self allow(subclass).to receive(:new).with(1, 2) end end + + context 'on a class that has redefined `self.method`' do + it 'allows the stubbing of :new' do + subclass = Class.new(klass) do + def self.method(*); end + end + + allow(subclass).to receive(:new) + end + end end end end From d26a8fda703e1e99b05e56cedd24c48c060ab119 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 27 Sep 2016 09:22:35 +1000 Subject: [PATCH 1038/1179] Changelog for #1119 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 29759f5f5..3ed6b1a9b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,8 @@ Bug Fixes: * Return the test double instance form `#freeze` (Alessandro Berardi, #1109) +* Allow the special logic for stubbing `new` to work when `.method` has + been redefined. (Proby, #1119) ### 3.5.0 / 2016-07-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta4...v3.5.0) From 0c87b91d32f4f827d2c1953935adcfd5468263a5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 28 Sep 2016 20:09:50 +1000 Subject: [PATCH 1039/1179] Updated common markdown files (from rspec-dev) [ci skip] (#1120) --- BUILD_DETAIL.md | 2 +- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 14 +++++++++++++- DEVELOPMENT.md | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/BUILD_DETAIL.md b/BUILD_DETAIL.md index 127d9e13d..04bb73e05 100644 --- a/BUILD_DETAIL.md +++ b/BUILD_DETAIL.md @@ -1,5 +1,5 @@ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 2585a1eea..17807af00 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,5 +1,5 @@ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f714cdb2..656612a26 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ @@ -23,3 +23,15 @@ If you'd like to help make RSpec better, here are some ways you can contribute: If you need help getting started, check out the [DEVELOPMENT](DEVELOPMENT.md) file for steps that will get you up and running. Thanks for helping us make RSpec better! + +## `Small` issues + +These issue are ones that we be believe are best suited for new contributors to +get started with. They represent a meaningful contribution to the project that +should not be too hard to pull off. + +## Maintenance branches + +Maintenance branches are how we manage the different supported point releases +of RSpec. As such, while they might look like good candidates to merge into +master, please do not open pull requests to merge them. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 9e31a0521..3903087a4 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,5 +1,5 @@ From c0bf45b5ceb179c51b0d38d7c91faf60a7a6048f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 9 Oct 2016 19:13:27 -0700 Subject: [PATCH 1040/1179] Release 3.6.0.beta1 --- Changelog.md | 4 ++-- lib/rspec/mocks/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3ed6b1a9b..d74e1dd68 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0...master) +### 3.6.0.beta1 / 2016-10-09 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0...v3.6.0.beta1) Bug Fixes: diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 259c006d8..d58ab7e8b 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.6.0.pre' + STRING = '3.6.0.beta1' end end end From 5f2f37c4673190f91734ba6230691fd7bf524097 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Mon, 7 Nov 2016 00:20:00 +0900 Subject: [PATCH 1041/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 7 ++++++- script/run_build | 7 ++++++- script/travis_functions.sh | 2 +- 8 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 82a8b7756..7ebdaf44f 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 42b8802b1..0f67e238f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index db29971a5..6ad2727d8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 529133c46..cec9669a2 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 297098dbf..d2dd97203 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 97605e323..2fe435d4a 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -65,6 +65,11 @@ function rspec_support_compatible { fi } +function additional_specs_available { + type run_additional_specs > /dev/null 2>&1 + return $? +} + function documentation_enforced { if [ -x ./bin/yard ]; then if is_mri_2plus; then diff --git a/script/run_build b/script/run_build index a544c54f6..385d1c892 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -13,6 +13,11 @@ fi fold "binstub check" check_binstubs fold "specs" run_specs_and_record_done + +if additional_specs_available; then + fold "additional specs" run_additional_specs +fi + fold "cukes" run_cukes if documentation_enforced; then diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 98713e234..ebea95328 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-07-14T09:50:14+10:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 48a3514d8d87011728e565c4f776e772226d4a06 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Mon, 7 Nov 2016 11:38:26 +0900 Subject: [PATCH 1042/1179] Updated travis build scripts (from rspec-dev) (#1125) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 4 ++-- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 7ebdaf44f..7bb9267bb 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 0f67e238f..3f9b79dae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 6ad2727d8..545202fc7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index cec9669a2..e609fc91a 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index d2dd97203..9f11ac5a8 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -28,7 +28,7 @@ function run_specs_and_record_done { # rspec-core needs to run with a special script that loads simplecov first, # so that it can instrument rspec-core's code before rspec-core has been loaded. - if [ -f script/rspec_with_simplecov ]; then + if [ -f script/rspec_with_simplecov ] && is_mri; then rspec_bin=script/rspec_with_simplecov fi; diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 2fe435d4a..aae5de37e 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 385d1c892..902911597 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index ebea95328..03bb90e6e 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T00:20:00+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From fa4eb34fb97db029832b0414d5d4002a56c152f7 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 11 Jul 2016 20:00:00 +1000 Subject: [PATCH 1043/1179] allow temporarily suppressing verification of partial doubles --- lib/rspec/mocks/configuration.rb | 6 ++++++ lib/rspec/mocks/example_methods.rb | 11 ++++++++++ lib/rspec/mocks/verifying_proxy.rb | 2 ++ spec/rspec/mocks/partial_double_spec.rb | 27 +++++++++++++++++++++++-- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index f72e84dc6..0d819e084 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -8,6 +8,7 @@ def initialize @verify_doubled_constant_names = false @transfer_nested_constants = false @verify_partial_doubles = false + @temporarily_suppressing_verification = false end # Sets whether RSpec will warn, ignore, or fail a test when @@ -153,6 +154,11 @@ def verify_partial_doubles? @verify_partial_doubles end + # @private + # Used to track wether we are temporarily suppressing verifying doubles + # with `without_verifying_partial_doubles { ... }` + attr_accessor :temporarily_suppressing_verification + if ::RSpec.respond_to?(:configuration) def color? ::RSpec.configuration.color_enabled? diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index e7c143d84..34292a3d4 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -282,6 +282,17 @@ def have_received(method_name, &block) Matchers::HaveReceived.new(method_name, &block) end + # Turns off the verifying of partial doubles for the duration of the + # block, this is useful in situations where methods are defined at run + # time and you wish to define stubs for them but not turn off partial + # doubles for the entire run suite. (e.g. view specs in rspec-rails). + def without_verifying_partial_doubles + Mocks.configuration.temporarily_suppressing_verification = true + yield + ensure + Mocks.configuration.temporarily_suppressing_verification = false + end + # @method expect # Used to wrap an object in preparation for setting a mock expectation # on it. diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index a3e421722..44b341ed8 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -36,6 +36,7 @@ def add_message_expectation(method_name, opts={}, &block) end def ensure_implemented(method_name) + return if Mocks.configuration.temporarily_suppressing_verification return unless method_reference[method_name].unimplemented? @error_generator.raise_unimplemented_error( @@ -46,6 +47,7 @@ def ensure_implemented(method_name) end def ensure_publicly_implemented(method_name, _object) + return if Mocks.configuration.temporarily_suppressing_verification ensure_implemented(method_name) visibility = method_reference[method_name].visibility diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 60c0a961b..47b8029df 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -377,6 +377,17 @@ def defined_private_method expect(object.send(:defined_private_method)).to eq("works") end + it 'can be temporarily supressed' do + without_verifying_partial_doubles do + expect(object).to receive(:fictitious_method) { 'works' } + end + expect(object.fictitious_method).to eq 'works' + + expect { + expect(object).to receive(:another_fictitious_method) { 'works' } + }.to raise_error RSpec::Mocks::MockExpectationError + end + it 'runs the before_verifying_double callbacks before verifying an expectation' do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| @@ -416,9 +427,9 @@ def defined_private_method end context "for a class" do - it "only runs the `before_verifying_doubles` callback for the class (not for superclasses)" do - subclass = Class.new(klass) + let(:subclass) { Class.new(klass) } + it "only runs the `before_verifying_doubles` callback for the class (not for superclasses)" do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.before_verifying_doubles(&probe) @@ -429,6 +440,18 @@ def defined_private_method an_object_having_attributes(:target => subclass) ) end + + it 'can be temporarily supressed' do + without_verifying_partial_doubles do + expect(subclass).to receive(:fictitious_method) { 'works' } + end + expect(subclass.fictitious_method).to eq 'works' + + expect { + expect(subclass).to receive(:another_fictitious_method) { 'works' } + }.to raise_error RSpec::Mocks::MockExpectationError + end + end it 'does not allow a non-existing method to be expected' do From 2ca10c7ce0663beafa17c8a33bdb1adf7ad5ffde Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 12 Jul 2016 14:46:56 +1000 Subject: [PATCH 1044/1179] 1.8.7 doesnt like verifying arguments of nonexistant implementations --- lib/rspec/mocks/verifying_proxy.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 44b341ed8..1cdbaf9be 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -195,6 +195,8 @@ def unimplemented? def self.for(object, method_name, proxy) if ClassNewMethodReference.applies_to?(method_name) { object } VerifyingExistingClassNewMethodDouble + elsif Mocks.configuration.temporarily_suppressing_verification + MethodDouble else self end.new(object, method_name, proxy) From bea81b9187911b0157436693cf4f6d397d925590 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 15 Sep 2016 21:45:30 +1000 Subject: [PATCH 1045/1179] rename config method to better reflect its purpose --- lib/rspec/mocks/configuration.rb | 8 ++++---- lib/rspec/mocks/example_methods.rb | 4 ++-- lib/rspec/mocks/verifying_proxy.rb | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 0d819e084..791841a7e 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -8,7 +8,7 @@ def initialize @verify_doubled_constant_names = false @transfer_nested_constants = false @verify_partial_doubles = false - @temporarily_suppressing_verification = false + @temporarily_suppressing_partial_double_verification = false end # Sets whether RSpec will warn, ignore, or fail a test when @@ -155,9 +155,9 @@ def verify_partial_doubles? end # @private - # Used to track wether we are temporarily suppressing verifying doubles - # with `without_verifying_partial_doubles { ... }` - attr_accessor :temporarily_suppressing_verification + # Used to track wether we are temporarily suppressing verifying partial + # doubles with `without_verifying_partial_doubles { ... }` + attr_accessor :temporarily_suppressing_partial_double_verification if ::RSpec.respond_to?(:configuration) def color? diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 34292a3d4..58fa20639 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -287,10 +287,10 @@ def have_received(method_name, &block) # time and you wish to define stubs for them but not turn off partial # doubles for the entire run suite. (e.g. view specs in rspec-rails). def without_verifying_partial_doubles - Mocks.configuration.temporarily_suppressing_verification = true + Mocks.configuration.temporarily_suppressing_partial_double_verification = true yield ensure - Mocks.configuration.temporarily_suppressing_verification = false + Mocks.configuration.temporarily_suppressing_partial_double_verification = false end # @method expect diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 1cdbaf9be..bdf2acd17 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -36,7 +36,7 @@ def add_message_expectation(method_name, opts={}, &block) end def ensure_implemented(method_name) - return if Mocks.configuration.temporarily_suppressing_verification + return if Mocks.configuration.temporarily_suppressing_partial_double_verification return unless method_reference[method_name].unimplemented? @error_generator.raise_unimplemented_error( @@ -47,7 +47,7 @@ def ensure_implemented(method_name) end def ensure_publicly_implemented(method_name, _object) - return if Mocks.configuration.temporarily_suppressing_verification + return if Mocks.configuration.temporarily_suppressing_partial_double_verification ensure_implemented(method_name) visibility = method_reference[method_name].visibility @@ -195,7 +195,7 @@ def unimplemented? def self.for(object, method_name, proxy) if ClassNewMethodReference.applies_to?(method_name) { object } VerifyingExistingClassNewMethodDouble - elsif Mocks.configuration.temporarily_suppressing_verification + elsif Mocks.configuration.temporarily_suppressing_partial_double_verification MethodDouble else self From 51e73e9ab336244a50b3612ecbadf66e6f39af66 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 10 Nov 2016 22:20:10 +1100 Subject: [PATCH 1046/1179] use shorter configuration method name without ing --- lib/rspec/mocks/configuration.rb | 4 ++-- lib/rspec/mocks/example_methods.rb | 4 ++-- lib/rspec/mocks/verifying_proxy.rb | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 791841a7e..394074d45 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -8,7 +8,7 @@ def initialize @verify_doubled_constant_names = false @transfer_nested_constants = false @verify_partial_doubles = false - @temporarily_suppressing_partial_double_verification = false + @temporarily_suppress_partial_double_verification = false end # Sets whether RSpec will warn, ignore, or fail a test when @@ -157,7 +157,7 @@ def verify_partial_doubles? # @private # Used to track wether we are temporarily suppressing verifying partial # doubles with `without_verifying_partial_doubles { ... }` - attr_accessor :temporarily_suppressing_partial_double_verification + attr_accessor :temporarily_suppress_partial_double_verification if ::RSpec.respond_to?(:configuration) def color? diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 58fa20639..455a989d1 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -287,10 +287,10 @@ def have_received(method_name, &block) # time and you wish to define stubs for them but not turn off partial # doubles for the entire run suite. (e.g. view specs in rspec-rails). def without_verifying_partial_doubles - Mocks.configuration.temporarily_suppressing_partial_double_verification = true + Mocks.configuration.temporarily_suppress_partial_double_verification = true yield ensure - Mocks.configuration.temporarily_suppressing_partial_double_verification = false + Mocks.configuration.temporarily_suppress_partial_double_verification = false end # @method expect diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index bdf2acd17..7171ee241 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -36,7 +36,7 @@ def add_message_expectation(method_name, opts={}, &block) end def ensure_implemented(method_name) - return if Mocks.configuration.temporarily_suppressing_partial_double_verification + return if Mocks.configuration.temporarily_suppress_partial_double_verification return unless method_reference[method_name].unimplemented? @error_generator.raise_unimplemented_error( @@ -47,7 +47,7 @@ def ensure_implemented(method_name) end def ensure_publicly_implemented(method_name, _object) - return if Mocks.configuration.temporarily_suppressing_partial_double_verification + return if Mocks.configuration.temporarily_suppress_partial_double_verification ensure_implemented(method_name) visibility = method_reference[method_name].visibility @@ -195,7 +195,7 @@ def unimplemented? def self.for(object, method_name, proxy) if ClassNewMethodReference.applies_to?(method_name) { object } VerifyingExistingClassNewMethodDouble - elsif Mocks.configuration.temporarily_suppressing_partial_double_verification + elsif Mocks.configuration.temporarily_suppress_partial_double_verification MethodDouble else self From 3281b6f1f36fefe7702975c6afc2050fdbe7ede8 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 10 Nov 2016 22:26:43 +1100 Subject: [PATCH 1047/1179] ensure we dont supress proper doubles --- lib/rspec/mocks/verifying_proxy.rb | 7 +++++-- spec/rspec/mocks/partial_double_spec.rb | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/verifying_proxy.rb b/lib/rspec/mocks/verifying_proxy.rb index 7171ee241..b39871c87 100644 --- a/lib/rspec/mocks/verifying_proxy.rb +++ b/lib/rspec/mocks/verifying_proxy.rb @@ -36,7 +36,6 @@ def add_message_expectation(method_name, opts={}, &block) end def ensure_implemented(method_name) - return if Mocks.configuration.temporarily_suppress_partial_double_verification return unless method_reference[method_name].unimplemented? @error_generator.raise_unimplemented_error( @@ -47,7 +46,6 @@ def ensure_implemented(method_name) end def ensure_publicly_implemented(method_name, _object) - return if Mocks.configuration.temporarily_suppress_partial_double_verification ensure_implemented(method_name) visibility = method_reference[method_name].visibility @@ -122,6 +120,11 @@ def initialize(object, expectation_ordering, optional_callback_invocation_strate optional_callback_invocation_strategy.call(@doubled_module) end + def ensure_implemented(_method_name) + return if Mocks.configuration.temporarily_suppress_partial_double_verification + super + end + def method_reference @method_doubles end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 47b8029df..6ba74e786 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -388,6 +388,16 @@ def defined_private_method }.to raise_error RSpec::Mocks::MockExpectationError end + specify 'temporarily supressing partial doubles does not affect normal verifying doubles' do + klass = Class.new + object = nil + without_verifying_partial_doubles do + expect { + object = instance_double(klass, :fictitious_method => 'works') + }.to raise_error RSpec::Mocks::MockExpectationError + end + end + it 'runs the before_verifying_double callbacks before verifying an expectation' do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| From 62af5b2ee82e5628643d04ced7a7441e3a7a9f94 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 15 Nov 2016 16:04:22 +1100 Subject: [PATCH 1048/1179] rename without_verifying_partial_doubles to without_partial_double_verification --- lib/rspec/mocks/configuration.rb | 2 +- lib/rspec/mocks/example_methods.rb | 2 +- spec/rspec/mocks/partial_double_spec.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index 394074d45..e2f0598fe 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -156,7 +156,7 @@ def verify_partial_doubles? # @private # Used to track wether we are temporarily suppressing verifying partial - # doubles with `without_verifying_partial_doubles { ... }` + # doubles with `without_partial_double_verification { ... }` attr_accessor :temporarily_suppress_partial_double_verification if ::RSpec.respond_to?(:configuration) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 455a989d1..a6f0feebd 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -286,7 +286,7 @@ def have_received(method_name, &block) # block, this is useful in situations where methods are defined at run # time and you wish to define stubs for them but not turn off partial # doubles for the entire run suite. (e.g. view specs in rspec-rails). - def without_verifying_partial_doubles + def without_partial_double_verification Mocks.configuration.temporarily_suppress_partial_double_verification = true yield ensure diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 6ba74e786..0f38bb1b8 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -378,7 +378,7 @@ def defined_private_method end it 'can be temporarily supressed' do - without_verifying_partial_doubles do + without_partial_double_verification do expect(object).to receive(:fictitious_method) { 'works' } end expect(object.fictitious_method).to eq 'works' @@ -391,7 +391,7 @@ def defined_private_method specify 'temporarily supressing partial doubles does not affect normal verifying doubles' do klass = Class.new object = nil - without_verifying_partial_doubles do + without_partial_double_verification do expect { object = instance_double(klass, :fictitious_method => 'works') }.to raise_error RSpec::Mocks::MockExpectationError @@ -452,7 +452,7 @@ def defined_private_method end it 'can be temporarily supressed' do - without_verifying_partial_doubles do + without_partial_double_verification do expect(subclass).to receive(:fictitious_method) { 'works' } end expect(subclass.fictitious_method).to eq 'works' From 414e9541f87f2cf11fd1693582b098063b4f838c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 15 Nov 2016 16:07:06 +1100 Subject: [PATCH 1049/1179] allow nested calls to without_partial_double_verification --- lib/rspec/mocks/example_methods.rb | 3 ++- spec/rspec/mocks/partial_double_spec.rb | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index a6f0feebd..8dae8222b 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -287,10 +287,11 @@ def have_received(method_name, &block) # time and you wish to define stubs for them but not turn off partial # doubles for the entire run suite. (e.g. view specs in rspec-rails). def without_partial_double_verification + original_state = Mocks.configuration.temporarily_suppress_partial_double_verification Mocks.configuration.temporarily_suppress_partial_double_verification = true yield ensure - Mocks.configuration.temporarily_suppress_partial_double_verification = false + Mocks.configuration.temporarily_suppress_partial_double_verification = original_state end # @method expect diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 0f38bb1b8..581bd816e 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -388,6 +388,21 @@ def defined_private_method }.to raise_error RSpec::Mocks::MockExpectationError end + it 'can be temporarily supressed and nested' do + without_partial_double_verification do + without_partial_double_verification do + expect(object).to receive(:fictitious_method) { 'works' } + end + expect(object).to receive(:other_fictitious_method) { 'works' } + end + expect(object.fictitious_method).to eq 'works' + expect(object.other_fictitious_method).to eq 'works' + + expect { + expect(object).to receive(:another_fictitious_method) { 'works' } + }.to raise_error RSpec::Mocks::MockExpectationError + end + specify 'temporarily supressing partial doubles does not affect normal verifying doubles' do klass = Class.new object = nil From 09ca65be39a3731b17fbdfb0ecee5c435a1124e5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 15 Nov 2016 16:12:53 +1100 Subject: [PATCH 1050/1179] cleanup partial double spec --- spec/rspec/mocks/partial_double_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index 581bd816e..aea06ca2f 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -404,11 +404,9 @@ def defined_private_method end specify 'temporarily supressing partial doubles does not affect normal verifying doubles' do - klass = Class.new - object = nil without_partial_double_verification do expect { - object = instance_double(klass, :fictitious_method => 'works') + instance_double(Class.new, :fictitious_method => 'works') }.to raise_error RSpec::Mocks::MockExpectationError end end From 8ecc778819f7add7e40eec5f0ce67e070c6a2edd Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 15 Nov 2016 16:36:39 +1100 Subject: [PATCH 1051/1179] benchmark showing method vs cache perf --- ...ssing_configuration_via_method_vs_cache.rb | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 benchmarks/accessing_configuration_via_method_vs_cache.rb diff --git a/benchmarks/accessing_configuration_via_method_vs_cache.rb b/benchmarks/accessing_configuration_via_method_vs_cache.rb new file mode 100644 index 000000000..90c82448e --- /dev/null +++ b/benchmarks/accessing_configuration_via_method_vs_cache.rb @@ -0,0 +1,52 @@ +require 'benchmark' + +n = 10_000 + +require 'rspec/mocks' + +# precache config +RSpec::Mocks.configuration + +Benchmark.benchmark do |bm| + puts "#{n} times - ruby #{RUBY_VERSION}" + + puts + puts "directly" + + 3.times do + bm.report do + n.times do + original_state = RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification + RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification = true + RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification = original_state + end + end + end + + puts + puts "with cached value" + + 3.times do + bm.report do + n.times do + config = RSpec::Mocks.configuration + original_state = config.temporarily_suppress_partial_double_verification + config.temporarily_suppress_partial_double_verification = true + config.temporarily_suppress_partial_double_verification = original_state + end + end + end +end + +__END__ +10000 times - ruby 2.3.1 + +directly + 0.000000 0.000000 0.000000 ( 0.002654) + 0.000000 0.000000 0.000000 ( 0.002647) + 0.010000 0.000000 0.010000 ( 0.002645) + +with cached value + 0.000000 0.000000 0.000000 ( 0.001386) + 0.000000 0.000000 0.000000 ( 0.001387) + 0.000000 0.000000 0.000000 ( 0.001399) From 94bf6da5575681f290c64c7aac705540f1ae35cb Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 18 Nov 2016 14:23:13 +1100 Subject: [PATCH 1052/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 4 ++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 7bb9267bb..9c5c33a3c 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 3f9b79dae..106487129 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -21,7 +21,7 @@ rvm: - 2.0.0 - 2.1 - 2.2.5 - - 2.3.1 + - 2.3.2 - 2.4.0-preview1 - ruby-head - ree diff --git a/appveyor.yml b/appveyor.yml index 545202fc7..5378784a7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e609fc91a..ce3ea551e 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 9f11ac5a8..15e9124e9 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index aae5de37e..a2b1fbbf4 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 902911597..63b6cc1da 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 03bb90e6e..a12b31953 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-07T11:05:32+09:00 from the rspec-dev repo. +# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From a9fd479ee4eb541f4ef63c966a5f9d9c93fc0f0e Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Sat, 19 Nov 2016 12:59:39 +0100 Subject: [PATCH 1053/1179] Add space to #ordered warning message - "willhave" => "will have" --- lib/rspec/mocks/message_expectation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index c824e4a4f..160f67346 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -330,7 +330,7 @@ def with(*args, &block) def ordered(&block) if type == :stub RSpec.warning( - "`allow(...).to receive(..).ordered` is not supported and will" \ + "`allow(...).to receive(..).ordered` is not supported and will " \ "have no effect, use `and_return(*ordered_values)` instead." ) end From 14fe614eaed020fe240510c1ec005a7b21031d62 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 22 Nov 2016 18:14:15 +1100 Subject: [PATCH 1054/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 4 ++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 9c5c33a3c..937a47a07 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. +# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 106487129..42d2eec64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. +# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -21,7 +21,7 @@ rvm: - 2.0.0 - 2.1 - 2.2.5 - - 2.3.2 + - 2.3.3 - 2.4.0-preview1 - ruby-head - ree diff --git a/appveyor.yml b/appveyor.yml index 5378784a7..31d5ac64d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. +# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index ce3ea551e..41301a501 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. +# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 15e9124e9..cd845b5c2 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. +# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index a2b1fbbf4..388bfc67c 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. +# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 63b6cc1da..a7d59b746 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. +# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index a12b31953..9567d661b 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-18T14:23:13+11:00 from the rspec-dev repo. +# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 65820ee363837ea0995b8767618de1ef7ed3a6f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 12 Dec 2016 09:19:57 -0800 Subject: [PATCH 1055/1179] Release v3.6.0.beta2 --- Changelog.md | 9 +++++++++ lib/rspec/mocks/version.rb | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d74e1dd68..6ddd0f639 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 3.6.0.beta2 / 2016-12-12 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta1...v3.6.0.beta2) + +Enhancements: + +* Add new `without_partial_double_verification { }` API that lets you + temporarily turn off partial double verification for an example. + (Jon Rowe, #1104) + ### 3.6.0.beta1 / 2016-10-09 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0...v3.6.0.beta1) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index d58ab7e8b..0b7e3d648 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.6.0.beta1' + STRING = '3.6.0.beta2' end end end From bb325180df392791ee58edc98949e49cd893befb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 26 Dec 2016 23:17:05 -0800 Subject: [PATCH 1056/1179] Ensure rubocop does not install on Ruby 2.4. rubocop 0.23 depends on a version of json with native extensions that fail to compile on Ruby 2.4. The platform directive does not prevent it from being installed on Ruby 2.4 for some reason. --- Gemfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index ff04cffa4..6cdfb1052 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,10 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp end gem 'yard', '~> 0.8.7', :require => false -gem 'rubocop', "~> 0.23.0", :platform => [:ruby_19, :ruby_20, :ruby_21] + +if RUBY_VERSION >= '1.9' && RUBY_VERSION <= '2.1' + gem 'rubocop', "~> 0.23.0" +end ### deps for rdoc.info group :documentation do From 1f7f4175e4f185d1a989dca0e8cd8bc6e6d4555f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 26 Dec 2016 23:26:58 -0800 Subject: [PATCH 1057/1179] Address Ruby 2.4 Fixnum changes. --- .../matching_arguments.feature | 2 +- spec/rspec/mocks/argument_matchers_spec.rb | 28 +++++++++---------- spec/rspec/mocks/matchers/receive_spec.rb | 14 +++++----- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/features/setting_constraints/matching_arguments.feature b/features/setting_constraints/matching_arguments.feature index abc67b265..219302fc1 100644 --- a/features/setting_constraints/matching_arguments.feature +++ b/features/setting_constraints/matching_arguments.feature @@ -17,7 +17,7 @@ Feature: Matching arguments | A subset of a hash | `with(hash_including(:a => 1))` | `foo(:a => 1, :b => 2)` | | An excluded subset of a hash | `with(hash_excluding(:a => 1))` | `foo(:b => 2)` | | A subset of an array | `with(array_including(:a, :b))` | `foo([:a, :b, :c])` | - | An instance of a specific class | `with(instance_of(Fixnum))` | `foo(3)` | + | An instance of a specific class | `with(instance_of(Integer))` | `foo(3)` | | An object with a given module in its ancestors list | `with(kind_of(Numeric))` | `foo(3)` | | Any RSpec matcher | `with()` | `foo()` | diff --git a/spec/rspec/mocks/argument_matchers_spec.rb b/spec/rspec/mocks/argument_matchers_spec.rb index 3d4c15499..3df91728d 100644 --- a/spec/rspec/mocks/argument_matchers_spec.rb +++ b/spec/rspec/mocks/argument_matchers_spec.rb @@ -59,19 +59,19 @@ module Mocks end describe "instance_of" do - it "accepts fixnum as instance_of(Fixnum)" do - expect(a_double).to receive(:random_call).with(instance_of(Fixnum)) - a_double.random_call(1) + it "accepts float as instance_of(Float)" do + expect(a_double).to receive(:random_call).with(instance_of(Float)) + a_double.random_call(1.1) end - it "does NOT accept fixnum as instance_of(Numeric)" do + it "does NOT accept float as instance_of(Numeric)" do expect(a_double).not_to receive(:random_call).with(instance_of(Numeric)) - a_double.random_call(1) + a_double.random_call(1.1) end - it "does NOT accept float as instance_of(Numeric)" do + it "does NOT accept integer as instance_of(Numeric)" do expect(a_double).not_to receive(:random_call).with(instance_of(Numeric)) - a_double.random_call(1.5) + a_double.random_call(1) end it "rejects non numeric", :reset => true do @@ -408,23 +408,23 @@ def ==(other) end it "matches a class against itself" do - expect(a_double).to receive(:foo).with(Fixnum) - a_double.foo(Fixnum) + expect(a_double).to receive(:foo).with(Float) + a_double.foo(Float) end it "fails a class against an unrelated class", :reset => true do - expect(a_double).to receive(:foo).with(Fixnum) + expect(a_double).to receive(:foo).with(Float) expect { a_double.foo(Hash) }.to fail end it "matches a class against an instance of itself" do - expect(a_double).to receive(:foo).with(Fixnum) - a_double.foo(3) + expect(a_double).to receive(:foo).with(Float) + a_double.foo(3.3) end it "fails a class against an object of a different type", :reset => true do - expect(a_double).to receive(:foo).with(Fixnum) - expect { a_double.foo(3.2) }.to fail + expect(a_double).to receive(:foo).with(Float) + expect { a_double.foo(3) }.to fail end it "fails with zero arguments", :reset => true do diff --git a/spec/rspec/mocks/matchers/receive_spec.rb b/spec/rspec/mocks/matchers/receive_spec.rb index 4285ceb5f..7937e666b 100644 --- a/spec/rspec/mocks/matchers/receive_spec.rb +++ b/spec/rspec/mocks/matchers/receive_spec.rb @@ -47,11 +47,11 @@ module Mocks end it 'allows the caller to constrain the received arguments by matcher' do - wrapped.to receive(:foo).with an_instance_of Fixnum + wrapped.to receive(:foo).with an_instance_of Float expect { - receiver.foo(1.1) - }.to raise_error(/expected.*\(an instance of Fixnum\)/) - receiver.foo(1) + receiver.foo(1) + }.to raise_error(/expected.*\(an instance of Float\)/) + receiver.foo(1.1) end it 'allows a `do...end` block implementation to be provided' do @@ -352,9 +352,9 @@ def receiver.method_missing(*); end # a poor man's stub... context "when a message is not received" do it 'sets up a message expectation that formats argument matchers correctly' do - wrapped.to receive(:foo).with an_instance_of Fixnum + wrapped.to receive(:foo).with an_instance_of Float expect { verify_all }.to( - raise_error(/expected: 1 time with arguments: \(an instance of Fixnum\)\n\s+received: 0 times$/) + raise_error(/expected: 1 time with arguments: \(an instance of Float\)\n\s+received: 0 times$/) ) end end @@ -467,7 +467,7 @@ def receiver.method_missing(*); end # a poor man's stub... let(:receiver) { klass.new } it 'sets up a message expectation that formats argument matchers correctly' do - wrapped.to receive(:foo).with an_instance_of Fixnum + wrapped.to receive(:foo).with an_instance_of Float expect { verify_all }.to raise_error(/should have received the following message\(s\) but didn't/) end end From f3c2e18fc1cf3a5018825a292c8698518fb4d25f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 27 Dec 2016 00:15:16 -0800 Subject: [PATCH 1058/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 5 ++--- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 937a47a07..10e215b59 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. +# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 42d2eec64..e219831c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. +# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -22,7 +22,7 @@ rvm: - 2.1 - 2.2.5 - 2.3.3 - - 2.4.0-preview1 + - 2.4.0 - ruby-head - ree - rbx @@ -43,7 +43,6 @@ matrix: env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - rvm: ruby-head - rvm: rbx - - rvm: 2.4.0-preview1 fast_finish: true branches: only: diff --git a/appveyor.yml b/appveyor.yml index 31d5ac64d..2bbb4c5a3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. +# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 41301a501..b09c3e13c 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. +# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index cd845b5c2..345e365b1 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. +# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 388bfc67c..0e0524116 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. +# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index a7d59b746..a5f47d657 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. +# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 9567d661b..cdf6b897e 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-11-22T18:14:15+11:00 from the rspec-dev repo. +# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 141caf2791e9acf3b2307246e8d75bdfe2e0cb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Glauco=20Cust=C3=B3dio?= Date: Tue, 3 Jan 2017 17:02:09 -0200 Subject: [PATCH 1059/1179] Update README.md (#1137) * Update README.md * Update README.md * Update README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index e4a795d93..e0017206a 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,21 @@ that iterates through them: order.calculate_total_price(double(:price => 1.99), double(:price => 2.99)) ``` +### Stubbing a chain of methods + +You can use `receive_message_chain` in place of `receive` to stub a chain of messages: + +```ruby +allow(double).to receive_message_chain("foo.bar") { :baz } +allow(double).to receive_message_chain(:foo, :bar => :baz) +allow(double).to receive_message_chain(:foo, :bar) { :baz } + +# Given any of the above forms: +double.foo.bar # => :baz +``` + +Chains can be arbitrarily long, which makes it quite painless to violate the Law of Demeter in violent ways, so you should consider any use of `receive_message_chain` a code smell. Even though not all code smells indicate real problems (think fluent interfaces), `receive_message_chain` still results in brittle examples. For example, if you write `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the implementation calls `foo.baz.bar`, the stub will not work. + ## Consecutive return values When a stub might be invoked more than once, you can provide additional From bac948e87a1ce8ccf62c586bee2710de59bb3fef Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 7 Jan 2017 13:16:11 +1100 Subject: [PATCH 1060/1179] Remove rubocop from 1.9.2 --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 6cdfb1052..bcf0295cb 100644 --- a/Gemfile +++ b/Gemfile @@ -14,7 +14,8 @@ end gem 'yard', '~> 0.8.7', :require => false -if RUBY_VERSION >= '1.9' && RUBY_VERSION <= '2.1' +if RUBY_VERSION >= '2' && RUBY_VERSION <= '2.1' + # todo upgrade rubocop and run on a recent version e.g. 2.3 or 2.4 gem 'rubocop', "~> 0.23.0" end From 82c1b78f18439eea0df6f7c767732845a37d2810 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 17 Jan 2017 17:11:16 +1100 Subject: [PATCH 1061/1179] pin ffi on 1.8 and 1.9 on windows --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index bcf0295cb..cecea0ebe 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,10 @@ if RUBY_VERSION >= '2' && RUBY_VERSION <= '2.1' gem 'rubocop', "~> 0.23.0" end +if RUBY_VERSION < '2.0.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) + gem 'ffi', '< 1.9.15' # allow ffi to be installed on older rubies on windows +end + ### deps for rdoc.info group :documentation do gem 'redcarpet', '2.1.1' unless RUBY_PLATFORM == 'java' From 5dacb0f67708e24d49cefa224ac39ed8fa4686cd Mon Sep 17 00:00:00 2001 From: Alex DeLaPena Date: Wed, 1 Feb 2017 17:03:55 -0800 Subject: [PATCH 1062/1179] Correct 'a' vs 'an' grammar mistakes. --- spec/rspec/mocks/should_syntax_spec.rb | 2 +- spec/rspec/mocks_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/mocks/should_syntax_spec.rb b/spec/rspec/mocks/should_syntax_spec.rb index d9de00399..255f5be21 100644 --- a/spec/rspec/mocks/should_syntax_spec.rb +++ b/spec/rspec/mocks/should_syntax_spec.rb @@ -228,7 +228,7 @@ def use_rspec_mocks end end - it "adds an class to the current space" do + it "adds a class to the current space" do expect { klass.any_instance }.to change { RSpec::Mocks.space.any_instance_recorders.size }.by(1) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index fd91a02e4..398e5aa84 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -138,7 +138,7 @@ end end - context "in a before(:all) with a unmet mock expectation" do + context "in a before(:all) with an unmet mock expectation" do before(:all) do capture_error do RSpec::Mocks.with_temporary_scope do From 90e1ed2e87027a8426bbdf055ce509e8b63f6c31 Mon Sep 17 00:00:00 2001 From: Alex DeLaPena Date: Wed, 1 Feb 2017 17:16:18 -0800 Subject: [PATCH 1063/1179] Correct "its" vs "it's" grammar mistakes. --- .rubocop_rspec_base.yml | 2 +- .../wrapping_the_original_implementation.feature | 2 +- lib/rspec/mocks/example_methods.rb | 4 ++-- lib/rspec/mocks/method_double.rb | 4 ++-- spec/rspec/mocks/matchers/have_received_spec.rb | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 10e215b59..fd618f2c7 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -89,7 +89,7 @@ PercentLiteralDelimiters: '%x': () # a shell command as a string # We have too many special cases where we allow generator methods or prefer a -# prefixed predicate due to it's improved readability. +# prefixed predicate due to its improved readability. PredicateName: Enabled: false diff --git a/features/configuring_responses/wrapping_the_original_implementation.feature b/features/configuring_responses/wrapping_the_original_implementation.feature index 740ff65a3..e413a3c6c 100644 --- a/features/configuring_responses/wrapping_the_original_implementation.feature +++ b/features/configuring_responses/wrapping_the_original_implementation.feature @@ -1,7 +1,7 @@ Feature: Wrapping the original implementation Use `and_wrap_original` to modify a partial double's original response. This can be useful - when you want to utilise an external object but mutate it's response. For example if an + when you want to utilise an external object but mutate its response. For example if an API returns a large amount of data and for test purposes you'd like to trim it down. You can also use it to configure the default response for most arguments, and then override that for specific arguments using `with`. diff --git a/lib/rspec/mocks/example_methods.rb b/lib/rspec/mocks/example_methods.rb index 8dae8222b..5531b28bf 100644 --- a/lib/rspec/mocks/example_methods.rb +++ b/lib/rspec/mocks/example_methods.rb @@ -392,7 +392,7 @@ def without_partial_double_verification def self.included(klass) klass.class_exec do # This gets mixed in so that if `RSpec::Matchers` is included in - # `klass` later, it's definition of `expect` will take precedence. + # `klass` later, its definition of `expect` will take precedence. include ExpectHost unless method_defined?(:expect) end end @@ -400,7 +400,7 @@ def self.included(klass) # @private def self.extended(object) # This gets extended in so that if `RSpec::Matchers` is included in - # `klass` later, it's definition of `expect` will take precedence. + # `klass` later, its definition of `expect` will take precedence. object.extend ExpectHost unless object.respond_to?(:expect) end diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 939af9829..3f418bfbe 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -207,8 +207,8 @@ def raise_method_not_stubbed_error # In Ruby 2.0.0 and above prepend will alter the method lookup chain. # We use an object's singleton class to define method doubles upon, - # however if the object has had it's singleton class (as opposed to - # it's actual class) prepended too then the the method lookup chain + # however if the object has had its singleton class (as opposed to + # its actual class) prepended too then the the method lookup chain # will look in the prepended module first, **before** the singleton # class. # diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 797ecdb09..f63e73bb6 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -612,7 +612,7 @@ module Mocks end describe "allow(...).to have_received" do - it "fails because its nonsensical" do + it "fails because it's nonsensical" do _expect { allow(double).to have_received(:some_method) }.to fail_with("Using allow(...) with the `have_received` matcher is not supported as it would have no effect.") @@ -620,7 +620,7 @@ module Mocks end describe "allow_any_instance_of(...).to have_received" do - it "fails because its nonsensical" do + it "fails because it's nonsensical" do _expect { allow_any_instance_of(double).to have_received(:some_method) }.to fail_with("Using allow_any_instance_of(...) with the `have_received` matcher is not supported.") From c01b6561964ee8dd572fab78fc6c73a5c43ecb78 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Feb 2017 00:03:00 -0800 Subject: [PATCH 1064/1179] Avoid warning about uninitialized instance variable. --- Changelog.md | 8 ++++++++ lib/rspec/mocks/configuration.rb | 1 + 2 files changed, 9 insertions(+) diff --git a/Changelog.md b/Changelog.md index 6ddd0f639..5e87481fa 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta2...master) + +Bug Fixes: + +* Fix "instance variable @color not initialized" warning when using + rspec-mocks without rspec-core. (Myron Marston, #1142) + ### 3.6.0.beta2 / 2016-12-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta1...v3.6.0.beta2) diff --git a/lib/rspec/mocks/configuration.rb b/lib/rspec/mocks/configuration.rb index e2f0598fe..5962215f8 100644 --- a/lib/rspec/mocks/configuration.rb +++ b/lib/rspec/mocks/configuration.rb @@ -9,6 +9,7 @@ def initialize @transfer_nested_constants = false @verify_partial_doubles = false @temporarily_suppress_partial_double_verification = false + @color = false end # Sets whether RSpec will warn, ignore, or fail a test when From 7681fe9c9dbc8e1bdda1f291606923dc6c10a12e Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Tue, 7 Feb 2017 20:05:57 -0600 Subject: [PATCH 1065/1179] [InstanceMethodStasher] Say method is owned by class when the owner is the object --- lib/rspec/mocks/instance_method_stasher.rb | 11 +++++++++++ spec/rspec/mocks/stub_spec.rb | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/rspec/mocks/instance_method_stasher.rb b/lib/rspec/mocks/instance_method_stasher.rb index 13e502a47..12edec2fa 100644 --- a/lib/rspec/mocks/instance_method_stasher.rb +++ b/lib/rspec/mocks/instance_method_stasher.rb @@ -128,6 +128,17 @@ def method_owned_by_klass? # Hence, we verify that the owner actually has the method defined. # If the given owner does not have the method defined, we assume # that the method is actually owned by @klass. + # + # On 1.8, aliased methods can also report the wrong owner. Example: + # module M + # def a; end + # module_function :a + # alias b a + # module_function :b + # end + # The owner of M.b is the raw Module object, instead of the expected + # singleton class of the module + return true if RUBY_VERSION < '1.9' && owner == @object owner == @klass || !(method_defined_on_klass?(owner)) end end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 37a59d9e0..70b45b00e 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -285,6 +285,25 @@ def self.say_hello expect(@class.existing_class_method).to eq(:original_value) end + it "restores existing aliased module_function methods" do + m = Module.new do + def mkdir_p + :mkdir_p + end + module_function :mkdir_p + + alias mkpath mkdir_p + + module_function :mkpath + end + + allow(m).to receive(:mkpath) { :stub_value } + allow(m).to receive(:mkdir_p) { :stub_value } + reset m + expect(m.mkpath).to eq(:mkdir_p) + expect(m.mkdir_p).to eq(:mkdir_p) + end + it "restores existing private class methods" do # See bug reports 8302 and 7805 allow(@class).to receive(:existing_private_class_method) { :stub_value } From aeee38af6433cda8db1c720a4991053ed03687e1 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 8 Feb 2017 15:30:18 +1100 Subject: [PATCH 1066/1179] Changelog for #1144 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 5e87481fa..5f42b3776 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Bug Fixes: * Fix "instance variable @color not initialized" warning when using rspec-mocks without rspec-core. (Myron Marston, #1142) +* Restore aliased module methods properly when stubbing on 1.8.7. + (Samuel Giddins, #1144) ### 3.6.0.beta2 / 2016-12-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta1...v3.6.0.beta2) From be8d5c92a200328addf5365d3421d2940eb711b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Costa?= Date: Fri, 24 Mar 2017 15:50:54 +0000 Subject: [PATCH 1067/1179] Add a nice error message when calling stub_const with something other than a string (#1148) * Added a nice error message when calling with something other than a string * Moved unless statement to the top of the conditional check. Updated specs. Use === for type check instead of Note that may not work if the constant_name is a class that overrides that method, or maybe it doesnt respond to it at all. With ===, we know for sure that it will work for any type check --- lib/rspec/mocks/mutate_const.rb | 4 ++++ spec/rspec/mocks/mutate_const_spec.rb | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/lib/rspec/mocks/mutate_const.rb b/lib/rspec/mocks/mutate_const.rb index 7cb1c9278..69a93037d 100644 --- a/lib/rspec/mocks/mutate_const.rb +++ b/lib/rspec/mocks/mutate_const.rb @@ -105,6 +105,10 @@ class ConstantMutator # so you can stub constants in other contexts (e.g. helper # classes). def self.stub(constant_name, value, options={}) + unless String === constant_name + raise ArgumentError, "`stub_const` requires a String, but you provided a #{constant_name.class.name}" + end + mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const) DefinedConstantReplacer else diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index 7786dd637..fe8d4128e 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -218,6 +218,10 @@ def change_const_value_to(value) it_behaves_like "loaded constant stubbing", "TestClassThatDefinesSend::C" end + it "requires a string argument" do + expect { stub_const(10, 1) }.to raise_error(ArgumentError, /requires a String/i) + end + context 'for a loaded unnested constant' do it_behaves_like "loaded constant stubbing", "TestClass" From 52a31037c5de8c1d8e9da597abf68c6b4822ed58 Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Fri, 7 Apr 2017 12:39:52 -0700 Subject: [PATCH 1068/1179] Correct messaging on nil message `allow_message_expectations_on_nil` lives in RSpec::Mocks configuration, not the global `config` object. When trying to do `config.allow_message_expectations_on_nil`, an error is raised as the method does not exist. --- lib/rspec/mocks/error_generator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index eb47aea3b..09f180f5c 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -214,8 +214,8 @@ def raise_expectation_on_nil_error(method_name) def expectation_on_nil_message(method_name) "An expectation of `:#{method_name}` was set on `nil`. " \ - "To allow expectations on `nil` and suppress this message, set `config.allow_message_expectations_on_nil` to `true`. " \ - "To disallow expectations on `nil`, set `config.allow_message_expectations_on_nil` to `false`" + "To allow expectations on `nil` and suppress this message, set `RSpec::Mocks.configuration.allow_message_expectations_on_nil` to `true`. " \ + "To disallow expectations on `nil`, set `RSpec::Mocks.configuration.allow_message_expectations_on_nil` to `false`" end # @private From b526864773f4065caec61a4d06bd988a66d99778 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 10 Apr 2017 21:23:37 +1000 Subject: [PATCH 1069/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 4 ++-- .travis.yml | 9 +++++---- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index fd618f2c7..a4522a0be 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. +# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects @@ -89,7 +89,7 @@ PercentLiteralDelimiters: '%x': () # a shell command as a string # We have too many special cases where we allow generator methods or prefer a -# prefixed predicate due to its improved readability. +# prefixed predicate due to it's improved readability. PredicateName: Enabled: false diff --git a/.travis.yml b/.travis.yml index e219831c3..533714921 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. +# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -20,9 +20,9 @@ rvm: - 1.9.3 - 2.0.0 - 2.1 - - 2.2.5 - - 2.3.3 - - 2.4.0 + - 2.2.7 + - 2.3.4 + - 2.4.1 - ruby-head - ree - rbx @@ -43,6 +43,7 @@ matrix: env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - rvm: ruby-head - rvm: rbx + - rvm: 2.4.0-preview1 fast_finish: true branches: only: diff --git a/appveyor.yml b/appveyor.yml index 2bbb4c5a3..2d2aa8d8a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. +# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index b09c3e13c..0742498a0 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. +# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 345e365b1..cf74066ce 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. +# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 0e0524116..6727a8cf6 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. +# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index a5f47d657..2ac952d06 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. +# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index cdf6b897e..3c61e2c2b 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2016-12-27T00:15:16-08:00 from the rspec-dev repo. +# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From bdf5405f16299c449e2a67fac3e1af53776e5d69 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 12 Apr 2017 21:53:57 +1000 Subject: [PATCH 1070/1179] fix issue where expect_any_instance_of could not constrain arguments --- lib/rspec/mocks/any_instance/expect_chain_chain.rb | 1 + spec/rspec/mocks/any_instance_spec.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lib/rspec/mocks/any_instance/expect_chain_chain.rb b/lib/rspec/mocks/any_instance/expect_chain_chain.rb index 0d134025f..bd96c17d2 100644 --- a/lib/rspec/mocks/any_instance/expect_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/expect_chain_chain.rb @@ -24,6 +24,7 @@ def create_message_expectation_on(instance) def invocation_order @invocation_order ||= { + :with => [nil], :and_return => [nil], :and_raise => [nil], :and_yield => [nil] diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 313b3e761..520d74472 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -123,6 +123,11 @@ def private_method; :private_method_return_value; end expect(klass.new.one.plus(1)).to eq(2) expect(klass.new.one.plus(2)).to eq(3) end + + it 'can use a chain of methods to perform an expectation' do + expect_any_instance_of(klass).to receive_message_chain('message1.message2').with('some args') + klass.new.message1.message2('some args') + end end end From 1457905dc9c155e628b9d97a541d8babac5d1a55 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 12 Apr 2017 21:57:46 +1000 Subject: [PATCH 1071/1179] changelog for #1156 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 5f42b3776..a4191dbfe 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,8 @@ Bug Fixes: rspec-mocks without rspec-core. (Myron Marston, #1142) * Restore aliased module methods properly when stubbing on 1.8.7. (Samuel Giddins, #1144) +* Allow a message chain expectation to be constrained by argument(s). + (Jon Rowe, #1156) ### 3.6.0.beta2 / 2016-12-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta1...v3.6.0.beta2) From 52ac45cce77e841b0e4394558d7761a6ad31f3ca Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 13 Apr 2017 21:07:42 +1000 Subject: [PATCH 1072/1179] refactor invocation order to not error on unknown methods --- lib/rspec/mocks/any_instance/expect_chain_chain.rb | 7 +------ lib/rspec/mocks/any_instance/expectation_chain.rb | 3 +-- lib/rspec/mocks/any_instance/stub_chain.rb | 3 +-- lib/rspec/mocks/any_instance/stub_chain_chain.rb | 7 +------ 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/lib/rspec/mocks/any_instance/expect_chain_chain.rb b/lib/rspec/mocks/any_instance/expect_chain_chain.rb index bd96c17d2..991fbc929 100644 --- a/lib/rspec/mocks/any_instance/expect_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/expect_chain_chain.rb @@ -23,12 +23,7 @@ def create_message_expectation_on(instance) end def invocation_order - @invocation_order ||= { - :with => [nil], - :and_return => [nil], - :and_raise => [nil], - :and_yield => [nil] - } + @invocation_order ||= {} end end end diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 02b55e8d6..0f0ffdda7 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -37,9 +37,8 @@ def create_message_expectation_on(instance) def invocation_order @invocation_order ||= { - :with => [nil], :and_return => [:with, nil], - :and_raise => [:with, nil] + :and_raise => [:with, nil], } end end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index 1e37576b0..3cb05170c 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -27,7 +27,6 @@ def create_message_expectation_on(instance) def invocation_order @invocation_order ||= { - :with => [nil], :and_return => [:with, nil], :and_raise => [:with, nil], :and_yield => [:with, :and_yield, nil], @@ -38,7 +37,7 @@ def invocation_order end def verify_invocation_order(rspec_method_name, *_args, &_block) - return if invocation_order[rspec_method_name].include?(last_message) + return if invocation_order.fetch(rspec_method_name, [nil]).include?(last_message) raise NoMethodError, "Undefined method #{rspec_method_name}" end end diff --git a/lib/rspec/mocks/any_instance/stub_chain_chain.rb b/lib/rspec/mocks/any_instance/stub_chain_chain.rb index e5a23efe5..7031e8c0a 100644 --- a/lib/rspec/mocks/any_instance/stub_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain_chain.rb @@ -15,12 +15,7 @@ def create_message_expectation_on(instance) end def invocation_order - @invocation_order ||= { - :with => [nil], - :and_return => [nil], - :and_raise => [nil], - :and_yield => [nil] - } + @invocation_order ||= {} end end end From 89cbdae11d0eb37a338937b87eba0219f6f77961 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 14 Apr 2017 12:57:12 +1000 Subject: [PATCH 1073/1179] extract invocation orders to frozen constants --- lib/rspec/mocks/any_instance/expect_chain_chain.rb | 2 +- lib/rspec/mocks/any_instance/expectation_chain.rb | 9 ++++++--- lib/rspec/mocks/any_instance/stub_chain.rb | 11 ++++++++--- lib/rspec/mocks/any_instance/stub_chain_chain.rb | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/rspec/mocks/any_instance/expect_chain_chain.rb b/lib/rspec/mocks/any_instance/expect_chain_chain.rb index 991fbc929..c467ba93a 100644 --- a/lib/rspec/mocks/any_instance/expect_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/expect_chain_chain.rb @@ -23,7 +23,7 @@ def create_message_expectation_on(instance) end def invocation_order - @invocation_order ||= {} + EmptyInvocationOrder end end end diff --git a/lib/rspec/mocks/any_instance/expectation_chain.rb b/lib/rspec/mocks/any_instance/expectation_chain.rb index 0f0ffdda7..edf854826 100644 --- a/lib/rspec/mocks/any_instance/expectation_chain.rb +++ b/lib/rspec/mocks/any_instance/expectation_chain.rb @@ -35,11 +35,14 @@ def create_message_expectation_on(instance) me end - def invocation_order - @invocation_order ||= { + ExpectationInvocationOrder = + { :and_return => [:with, nil], :and_raise => [:with, nil], - } + }.freeze + + def invocation_order + ExpectationInvocationOrder end end end diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index 3cb05170c..85e767828 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -25,15 +25,20 @@ def create_message_expectation_on(instance) stub end - def invocation_order - @invocation_order ||= { + InovcationOrder = + { :and_return => [:with, nil], :and_raise => [:with, nil], :and_yield => [:with, :and_yield, nil], :and_throw => [:with, nil], :and_call_original => [:with, nil], :and_wrap_original => [:with, nil] - } + }.freeze + + EmptyInvocationOrder = {}.freeze + + def invocation_order + InovcationOrder end def verify_invocation_order(rspec_method_name, *_args, &_block) diff --git a/lib/rspec/mocks/any_instance/stub_chain_chain.rb b/lib/rspec/mocks/any_instance/stub_chain_chain.rb index 7031e8c0a..495511c09 100644 --- a/lib/rspec/mocks/any_instance/stub_chain_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain_chain.rb @@ -15,7 +15,7 @@ def create_message_expectation_on(instance) end def invocation_order - @invocation_order ||= {} + EmptyInvocationOrder end end end From c79eda7323558641e1516ac8c287b8faaf8e1f3b Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 May 2017 19:55:48 -0400 Subject: [PATCH 1074/1179] Updates changelog for v3.6.0 [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index a4191dbfe..9ce35ba9d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta2...master) +### 3.6.0 / 2017-05-04 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta2...v3.6.0) Bug Fixes: From a70af7d2c62b39e0f5e6cff8953666acfb5dcc8e Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 May 2017 19:56:40 -0400 Subject: [PATCH 1075/1179] Releases 3.6.0 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 0b7e3d648..598b9f977 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.6.0.beta2' + STRING = '3.6.0' end end end From cd7adc9fe1c418c2fe04d32f0961c615333af7aa Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 4 May 2017 20:11:09 -0400 Subject: [PATCH 1076/1179] Bump version to 3.7.0.pre --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 598b9f977..f50a6781f 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.6.0' + STRING = '3.7.0.pre' end end end From 38015e1aa82bd418581db5df565ef369d43b16ad Mon Sep 17 00:00:00 2001 From: Pat Allan Date: Wed, 28 Jun 2017 12:32:28 +1000 Subject: [PATCH 1077/1179] Updates for frozen-string-literal compatibility. (#1165) --- lib/rspec/mocks/error_generator.rb | 14 +++++++------- spec/rspec/mocks/any_instance_spec.rb | 8 ++++---- spec/rspec/mocks/diffing_spec.rb | 2 +- ...ssage_expectation_string_representation_spec.rb | 2 +- spec/rspec/mocks_spec.rb | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 09f180f5c..06ab1a046 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -57,10 +57,10 @@ def raise_unexpected_message_args_error(expectation, args_for_multiple_calls, so # @private def raise_missing_default_stub_error(expectation, args_for_multiple_calls) - message = error_message(expectation, args_for_multiple_calls) - message << "\n Please stub a default value first if message might be received with other args as well. \n" - - __raise message + __raise( + error_message(expectation, args_for_multiple_calls) + + "\n Please stub a default value first if message might be received with other args as well. \n" + ) end # @private @@ -69,7 +69,7 @@ def raise_similar_message_args_error(expectation, args_for_multiple_calls, backt end def default_error_message(expectation, expected_args, actual_args) - "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}" + "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}".dup end # rubocop:disable Style/ParameterLists @@ -87,14 +87,14 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc def raise_unimplemented_error(doubled_module, method_name, object) message = case object when InstanceVerifyingDouble - "the %s class does not implement the instance method: %s" << + "the %s class does not implement the instance method: %s".dup << if ObjectMethodReference.for(doubled_module, method_name).implemented? ". Perhaps you meant to use `class_double` instead?" else "" end when ClassVerifyingDouble - "the %s class does not implement the class method: %s" << + "the %s class does not implement the class method: %s".dup << if InstanceMethodReference.for(doubled_module, method_name).implemented? ". Perhaps you meant to use `instance_double` instead?" else diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 520d74472..c269c142d 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -378,12 +378,12 @@ def private_method; :private_method_return_value; end it "works with the non-standard constructor \"\"" do allow_any_instance_of(String).to receive(:foo).and_return(1) - expect("".foo).to eq(1) + expect("".dup.foo).to eq(1) end it "works with the non-standard constructor \'\'" do allow_any_instance_of(String).to receive(:foo).and_return(1) - expect(''.foo).to eq(1) + expect(''.dup.foo).to eq(1) end it "works with the non-standard constructor module" do @@ -545,10 +545,10 @@ def inspect it "does not set the expectation on every instance" do # Setup an unrelated object of the same class that won't receive the expected message. - allow('non-related object').to receive(:non_related_method) + allow('non-related object'.dup).to receive(:non_related_method) expect_any_instance_of(Object).to receive(:foo) - 'something'.foo + 'something'.dup.foo end it "does not modify the return value of stubs set on an instance" do diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index 70f985fd7..fe4454d0d 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -157,7 +157,7 @@ def inspect expect(RSpec::Support.is_a_matcher?(collab)).to be true collab_inspect = collab.inspect - collab_pp = PP.pp(collab, "").strip + collab_pp = PP.pp(collab, "".dup).strip expect(d).to receive(:foo).with(collab) expect { diff --git a/spec/rspec/mocks/message_expectation_string_representation_spec.rb b/spec/rspec/mocks/message_expectation_string_representation_spec.rb index 0a3bcafe1..998fb7b6f 100644 --- a/spec/rspec/mocks/message_expectation_string_representation_spec.rb +++ b/spec/rspec/mocks/message_expectation_string_representation_spec.rb @@ -10,7 +10,7 @@ module Mocks end example "for a raw message expectation on a partial double" do - expect(allow("partial double").to receive(:foo)).to have_string_representation( + expect(allow("partial double".dup).to receive(:foo)).to have_string_representation( '#' ) end diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 398e5aa84..533a475dc 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -45,7 +45,7 @@ describe ".teardown" do it "resets method stubs" do - string = "foo" + string = "foo".dup allow(string).to receive(:bar) RSpec::Mocks.teardown expect { string.bar }.to raise_error(NoMethodError) @@ -56,7 +56,7 @@ RSpec::Mocks.teardown RSpec::Mocks.teardown - string = "foo" + string = "foo".dup expect { allow(string).to receive(:bar) }.to raise_error(RSpec::Mocks::OutsideOfExampleError) RSpec::Mocks.setup @@ -157,10 +157,10 @@ before(:all) do RSpec::Mocks.with_temporary_scope do allow_any_instance_of(String).to receive(:sum_with) { |val, x| val + x } - @sum = "foo".sum_with("bar") + @sum = "foo".dup.sum_with("bar") end - capture_error { "you".sum_with("me") } + capture_error { "you".dup.sum_with("me") } end it 'allows the stub to be used' do From a4b0b7f9852fa7d6950864ecd6f46a8b2ffea9ce Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 28 Jun 2017 23:45:37 -0700 Subject: [PATCH 1078/1179] Update changelog. [ci skip] --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index 9ce35ba9d..60b4fa050 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### 3.7 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0...master) + +Enhancements: + +* Improve compatibility with `--enable-frozen-string-literal` option + on Ruby 2.3+. (Pat Allan, #1165) + ### 3.6.0 / 2017-05-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta2...v3.6.0) From 47f53d38c8a2e3ef1b2b16b51936476004db4c0f Mon Sep 17 00:00:00 2001 From: Aaron Rosenberg Date: Sat, 8 Jul 2017 05:07:31 +0100 Subject: [PATCH 1079/1179] Allow hash matchers to work with Hash descendants (#1167) Instead of checking if the object passed to ArgumentMatchers#anythingize_lonely_key _is_ strictly a Hash, check if it inherits from Hash using Case Equality inherited from `Module.===` [fixes #1166] --- lib/rspec/mocks/argument_matchers.rb | 2 +- spec/rspec/mocks/hash_excluding_matcher_spec.rb | 3 +++ spec/rspec/mocks/hash_including_matcher_spec.rb | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index 8fcfd02e8..f86dcd370 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -116,7 +116,7 @@ def kind_of(klass) # @private def self.anythingize_lonely_keys(*args) - hash = args.last.class == Hash ? args.delete_at(-1) : {} + hash = Hash === args.last ? args.delete_at(-1) : {} args.each { | arg | hash[arg] = AnyArgMatcher::INSTANCE } hash end diff --git a/spec/rspec/mocks/hash_excluding_matcher_spec.rb b/spec/rspec/mocks/hash_excluding_matcher_spec.rb index c4e52fd64..fc0faed05 100644 --- a/spec/rspec/mocks/hash_excluding_matcher_spec.rb +++ b/spec/rspec/mocks/hash_excluding_matcher_spec.rb @@ -28,6 +28,9 @@ module ArgumentMatchers expect(hash_not_including(:a, :b, :c)).to be === { :d => 7 } end + it "matches against classes inheriting from Hash" do + expect(hash_not_including(Class.new(Hash)[:c, 1])).not_to be === {:c => 1} + end end describe "failing" do diff --git a/spec/rspec/mocks/hash_including_matcher_spec.rb b/spec/rspec/mocks/hash_including_matcher_spec.rb index 0874366f9..ad5e8587a 100644 --- a/spec/rspec/mocks/hash_including_matcher_spec.rb +++ b/spec/rspec/mocks/hash_including_matcher_spec.rb @@ -22,6 +22,10 @@ module ArgumentMatchers expect(hash_including(:a => 1)).to be === {:a => 1, :b => 2} end + it "matches against classes inheriting from Hash" do + expect(hash_including(Class.new(Hash)[:a, 1])).to be === {:a => 1} + end + describe "when matching against other matchers" do it "matches an int against anything()" do expect(hash_including(:a => anything, :b => 2)).to be === {:a => 1, :b => 2} From 4847ef067b60e3725a9e4819b9d0ee0f5c5262eb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 7 Jul 2017 21:08:59 -0700 Subject: [PATCH 1080/1179] Changelog for #1167. [ci skip] --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 60b4fa050..6bbbc91ac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,11 @@ Enhancements: * Improve compatibility with `--enable-frozen-string-literal` option on Ruby 2.3+. (Pat Allan, #1165) +Bug Fixes: + +* Fix `hash_including` and `hash_excluding` so that they work against + subclasses of `Hash`. (Aaron Rosenberg, #1167) + ### 3.6.0 / 2017-05-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta2...v3.6.0) From d8e2d4778eeb4117bea8a1bc9135a44720e65505 Mon Sep 17 00:00:00 2001 From: Zhong Zheng Date: Mon, 10 Jul 2017 15:12:34 +1000 Subject: [PATCH 1081/1179] Update README.md (#1169) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0017206a..1af8adf4e 100644 --- a/README.md +++ b/README.md @@ -257,7 +257,7 @@ expect(double).to receive(:msg).with("B", 2, 4) ## Argument Matchers Arguments that are passed to `with` are compared with actual arguments -received using ==. In cases in which you want to specify things about the +received using ===. In cases in which you want to specify things about the arguments rather than the arguments themselves, you can use any of the matchers that ship with rspec-expectations. They don't all make syntactic sense (they were primarily designed for use with RSpec::Expectations), but From 0820af734f1a8926b0fee4fa4dd3e00ff70b4990 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 16 Jul 2017 07:28:13 +0700 Subject: [PATCH 1082/1179] Updated travis build scripts (from rspec-dev) (#1172) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 21 +++++++-------------- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 14 insertions(+), 21 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index a4522a0be..9eb65c860 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. +# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 533714921..45c5280f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. +# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -26,25 +26,18 @@ rvm: - ruby-head - ree - rbx + - jruby + - jruby-head + - jruby-18mode + - jruby-9.1.2.0 matrix: - include: - - rvm: jruby - env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false -Xcompat.version=2.0' - - rvm: jruby-head - env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - - rvm: jruby-18mode - env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - - rvm: jruby - env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - - rvm: jruby-9.1.2.0 - env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' allow_failures: - rvm: jruby-head - env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false' - rvm: ruby-head - rvm: rbx - - rvm: 2.4.0-preview1 fast_finish: true +env: + - JRUBY_OPTS='--dev' branches: only: - master diff --git a/appveyor.yml b/appveyor.yml index 2d2aa8d8a..1a085073f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. +# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 0742498a0..a5855cedb 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. +# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index cf74066ce..08cf64c3c 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. +# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 6727a8cf6..9e8809bd2 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. +# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 2ac952d06..b7d36b63c 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. +# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 3c61e2c2b..7b229109d 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-04-10T21:23:37+10:00 from the rspec-dev repo. +# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 2f642786a93129e3408d4dc4843fb38409b90f88 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Thu, 10 Aug 2017 01:31:46 +0900 Subject: [PATCH 1083/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 12 +++++++----- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 9eb65c860..4e13b4cc7 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. +# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 45c5280f8..757ae1664 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. +# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -28,16 +28,18 @@ rvm: - rbx - jruby - jruby-head - - jruby-18mode - - jruby-9.1.2.0 + - jruby-1.7 +env: + - JRUBY_OPTS='--dev' matrix: + include: + - rvm: jruby-1.7 + env: JRUBY_OPTS='--dev --1.8' allow_failures: - rvm: jruby-head - rvm: ruby-head - rvm: rbx fast_finish: true -env: - - JRUBY_OPTS='--dev' branches: only: - master diff --git a/appveyor.yml b/appveyor.yml index 1a085073f..db8267796 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. +# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index a5855cedb..950e0ab81 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. +# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 08cf64c3c..c4e6578ab 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. +# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 9e8809bd2..87401e122 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. +# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index b7d36b63c..a2103ac06 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. +# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 7b229109d..c6d054ed6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-07-15T20:27:05+07:00 from the rspec-dev repo. +# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From fa910249324822f2ebaac9ab9b7c10a8f6b7b701 Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Fri, 15 Sep 2017 10:18:21 +0900 Subject: [PATCH 1084/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 ++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4e13b4cc7..6aeff9b35 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. +# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 757ae1664..7e29752bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. +# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -8,6 +8,7 @@ cache: directories: - ../bundle before_install: + - unset _JAVA_OPTIONS - "script/clone_all_rspec_repos" # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it, the excluded versions are broken on Ruby 2.3 - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler --version "1.11.2"; fi diff --git a/appveyor.yml b/appveyor.yml index db8267796..90df32fdb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. +# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 950e0ab81..08a1b6cd3 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. +# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index c4e6578ab..b1d186639 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. +# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 87401e122..ff44c957c 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. +# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index a2103ac06..5f2f32cd5 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. +# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index c6d054ed6..f3fbcb972 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-08-10T01:31:46+09:00 from the rspec-dev repo. +# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 297dd60fcee36d7eb87b3b03e8dad2e8736a344d Mon Sep 17 00:00:00 2001 From: Yuji Nakayama Date: Fri, 15 Sep 2017 10:39:14 +0900 Subject: [PATCH 1085/1179] Mark a pending example on JRuby 9000 fixed --- spec/rspec/mocks_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 533a475dc..355a655b7 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -132,7 +132,7 @@ expect(defined?(ValueY)).to be_falsey end - it 'does not allow the stubbed constants to be used after the scope in before(:all)', :pending => RSpec::Support::Ruby.jruby_9000? do + it 'does not allow the stubbed constants to be used after the scope in before(:all)' do expect(@error).to be_a(NameError) expect(@error.message).to include("ValueX") end From 7b8bb8010cee0fc1a15982e90f3793105cff1f07 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 17 Oct 2017 15:51:50 +0100 Subject: [PATCH 1086/1179] Updates changelog for v3.7.0 [ci skip] --- Changelog.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6bbbc91ac..e8ef1f8bc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ -### 3.7 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0...master) +### 3.8 Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...master) + +### 3.7.0 / 2017-10-17 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0...v3.7.0) Enhancements: From df6ce2cc4a4bb2f5d62a2490fe4f2c290c22590c Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 17 Oct 2017 15:53:06 +0100 Subject: [PATCH 1087/1179] Releases 3.7.0 --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index f50a6781f..b563c565e 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.7.0.pre' + STRING = '3.7.0' end end end From 131c1bc0e3b74b6b0f4ba4c82e4b2f15c1e145a8 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Tue, 17 Oct 2017 16:20:38 +0100 Subject: [PATCH 1088/1179] Bump version to 3.8.pre --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index b563c565e..41431158a 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.7.0' + STRING = '3.8.pre' end end end From 066717925636251a1632796dd04207d3322cd677 Mon Sep 17 00:00:00 2001 From: Kenichi Kamiya Date: Fri, 3 Nov 2017 13:22:07 +0900 Subject: [PATCH 1089/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 10 +++++----- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 6aeff9b35..df1188ca1 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 7e29752bb..5649be604 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -22,11 +22,11 @@ rvm: - 2.0.0 - 2.1 - 2.2.7 - - 2.3.4 - - 2.4.1 + - 2.3.5 + - 2.4.2 - ruby-head - ree - - rbx + - rbx-3 - jruby - jruby-head - jruby-1.7 @@ -39,7 +39,7 @@ matrix: allow_failures: - rvm: jruby-head - rvm: ruby-head - - rvm: rbx + - rvm: rbx-3 fast_finish: true branches: only: diff --git a/appveyor.yml b/appveyor.yml index 90df32fdb..9dd01f24a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 08a1b6cd3..4a73220c7 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index b1d186639..da8aa31c3 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index ff44c957c..43e4e03cb 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 5f2f32cd5..4c50cf5d4 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index f3fbcb972..f6d10c767 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-09-15T10:18:21+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From d4d0c1a3f3ba019242dc2579b110b3b4d179bb39 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 13 Nov 2017 13:14:08 +1100 Subject: [PATCH 1090/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 4 ++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index df1188ca1..637c91696 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 5649be604..0d427b482 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -27,7 +27,7 @@ rvm: - ruby-head - ree - rbx-3 - - jruby + - jruby-9.1.7.0 # pin JRuby to this until travis/rvm can install later versions - jruby-head - jruby-1.7 env: diff --git a/appveyor.yml b/appveyor.yml index 9dd01f24a..861f476c6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 4a73220c7..1b8c8a2b4 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index da8aa31c3..80fb33266 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 43e4e03cb..ed8771a92 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 4c50cf5d4..5d12de802 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index f6d10c767..ce0c402f9 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-03T13:22:06+09:00 from the rspec-dev repo. +# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 27f3649a093a07cbfca107f899d9f436b3403782 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 15 Nov 2017 08:26:44 +1100 Subject: [PATCH 1091/1179] Pending warning check on JRuby --- spec/rspec/mocks_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 355a655b7..8257ba994 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -24,6 +24,13 @@ /rbconfig/ # loaded by rspec-support ] do + if RSpec::Support::Ruby.jruby? && JRUBY_VERSION =~ /9\.1\.7\.0/ + before(:example, :description => /spec files/) do + pending "JRuby 9.1.7.0 currently generates a circular warning which" \ + " is unrelated to our suite." + end + end + if RUBY_VERSION == '1.9.2' before(:example, :description => /spec files/) do pending "Loading psych and syck on 1.9.2 (as our test suite does) triggers warnings" From 7ee09a8367b9ede379fc1b3b3333631085d6c833 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 21 Nov 2017 14:21:55 +1100 Subject: [PATCH 1092/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 24 +++++++++++++++--------- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 637c91696..11926c2e3 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. +# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 0d427b482..5640684e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. +# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 861f476c6..8558b71a0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. +# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -14,20 +14,26 @@ branches: # Disable normal Windows builds in favor of our test script. build: off +cache: + - vendor/bundle + install: - - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - - ruby --version - - gem --version - - gem install bundler -v "!= 1.12.0, != 1.12.1, != 1.12.2, != 1.12.3" - - bundler --version + - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% + - bundle config --local path vendor/bundle - bundle install - cinst ansicon +before_test: + - ruby --version + - gem --version + - bundle --version + test_script: - bundle exec rspec --backtrace environment: matrix: - # ruby_version: '20' doesn't work for some reason - - ruby_version: '193' - - ruby_version: '21' + - ruby_version: 193 + - ruby_version: 200 + - ruby_version: 21 + - ruby_version: 22 diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 1b8c8a2b4..a8b29cb8f 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. +# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 80fb33266..da940fb46 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. +# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index ed8771a92..821a1469f 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. +# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 5d12de802..e2801d910 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. +# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index ce0c402f9..484bc61ce 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-13T13:14:08+11:00 from the rspec-dev repo. +# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From b2a84992c36e65f12c720239f5ac1e83aaa914f2 Mon Sep 17 00:00:00 2001 From: James Crisp Date: Thu, 30 Nov 2017 21:24:50 +1100 Subject: [PATCH 1093/1179] Fix specs for diffs showing keys in alphabetic order for PR https://github.com/rspec/rspec-support/pull/334 --- spec/rspec/mocks/diffing_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index fe4454d0d..e2a1b8a49 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -59,7 +59,7 @@ end end - let(:expected_hash) { {:foo => :bar, :baz => :quz} } + let(:expected_hash) { {:baz => :quz, :foo => :bar } } let(:actual_hash) { {:bad => :hash} } From 5b22403caf74495a832a4ef6de88f4c84513e154 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Wed, 20 Dec 2017 14:37:46 +0100 Subject: [PATCH 1094/1179] Misspelling: overridden --- Changelog.md | 3 ++- .../calling_the_original_implementation.feature | 4 ++-- .../wrapping_the_original_implementation.feature | 4 ++-- .../mocks/combining_implementation_instructions_spec.rb | 8 ++++---- spec/rspec/mocks/to_ary_spec.rb | 2 +- .../class_double_with_class_loaded_spec.rb | 8 ++++---- .../instance_double_with_class_loaded_spec.rb | 4 ++-- spec/support/doubled_classes.rb | 2 +- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Changelog.md b/Changelog.md index e8ef1f8bc..a5475d458 100644 --- a/Changelog.md +++ b/Changelog.md @@ -896,7 +896,8 @@ Bug fixes * Fix `should_receive` so that when it is called on an `as_null_object` double with no implementation, and there is a previous explicit stub for the same method, the explicit stub remains (rather than being - overriden with the null object implementation--`return self`). (Myron Marston) + overridden with the null object implementation--`return self`). (Myron + Marston) ### 2.11.0 / 2012-07-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.1...v2.11.0) diff --git a/features/configuring_responses/calling_the_original_implementation.feature b/features/configuring_responses/calling_the_original_implementation.feature index ecadf355c..aca7cd6a6 100644 --- a/features/configuring_responses/calling_the_original_implementation.feature +++ b/features/configuring_responses/calling_the_original_implementation.feature @@ -33,13 +33,13 @@ Feature: Calling the original implementation When I run `rspec spec/and_call_original_spec.rb` Then the examples should all pass - Scenario: `and_call_original` can configure a default response that can be overriden for specific args + Scenario: `and_call_original` can configure a default response that can be overridden for specific args Given a file named "spec/and_call_original_spec.rb" with: """ruby require 'calculator' RSpec.describe "and_call_original" do - it "can be overriden for specific arguments using #with" do + it "can be overridden for specific arguments using #with" do allow(Calculator).to receive(:add).and_call_original allow(Calculator).to receive(:add).with(2, 3).and_return(-5) diff --git a/features/configuring_responses/wrapping_the_original_implementation.feature b/features/configuring_responses/wrapping_the_original_implementation.feature index e413a3c6c..e9361a3c3 100644 --- a/features/configuring_responses/wrapping_the_original_implementation.feature +++ b/features/configuring_responses/wrapping_the_original_implementation.feature @@ -34,13 +34,13 @@ Feature: Wrapping the original implementation When I run `rspec spec/and_wrap_original_spec.rb` Then the examples should all pass - Scenario: `and_wrap_original` can configure a default response that can be overriden for specific args + Scenario: `and_wrap_original` can configure a default response that can be overridden for specific args Given a file named "spec/and_wrap_original_spec.rb" with: """ruby require 'api' RSpec.describe "and_wrap_original" do - it "can be overriden for specific arguments using #with" do + it "can be overridden for specific arguments using #with" do allow(API).to receive(:solve_for).and_wrap_original { |m, *args| m.call(*args).first(5) } allow(API).to receive(:solve_for).with(2).and_return([3]) diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index 27d37cb9d..d5cf2072d 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -129,17 +129,17 @@ def verify_combined_implementation let(:stubbed_double) { allow(the_dbl).to receive(:foo) } before { stubbed_double.and_return(1) } - it 'allows the terminal action to be overriden to `and_return(y)`' do + it 'allows the terminal action to be overridden to `and_return(y)`' do stubbed_double.and_return(3) expect(the_dbl.foo).to eq(3) end - it 'allows the terminal action to be overriden to `and_raise(y)`' do + it 'allows the terminal action to be overridden to `and_raise(y)`' do stubbed_double.and_raise("boom") expect { the_dbl.foo }.to raise_error("boom") end - it 'allows the terminal action to be overriden to `and_throw(y)`' do + it 'allows the terminal action to be overridden to `and_throw(y)`' do stubbed_double.and_throw(:bar) expect { the_dbl.foo }.to throw_symbol(:bar) end @@ -157,7 +157,7 @@ def verify_combined_implementation end end - it 'warns when the inner implementation block is overriden' do + it 'warns when the inner implementation block is overridden' do expect(RSpec).to receive(:warning).with(/overriding a previous stub implementation of `foo`.*#{__FILE__}:#{__LINE__ + 1}/) allow(double).to receive(:foo).with(:arg) { :with_block }.at_least(:once) { :at_least_block } end diff --git a/spec/rspec/mocks/to_ary_spec.rb b/spec/rspec/mocks/to_ary_spec.rb index 4b060afb7..49984d7d7 100644 --- a/spec/rspec/mocks/to_ary_spec.rb +++ b/spec/rspec/mocks/to_ary_spec.rb @@ -5,7 +5,7 @@ expect(obj.to_ary).to be(:non_nil_value) end - it "responds when overriden" do + it "responds when overridden" do allow(obj).to receive(:to_ary) { :non_nil_value } expect(obj).to respond_to(:to_ary) end diff --git a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb index f12dac635..60a5faefb 100644 --- a/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb @@ -187,14 +187,14 @@ class << self end end - context "when given a class that has an overriden `#name` method" do + context "when given a class that has an overridden `#name` method" do it "properly verifies" do - check_verification class_double(LoadedClassWithOverridenName) + check_verification class_double(LoadedClassWithOverriddenName) end it "can still stub the const" do - class_double(LoadedClassWithOverridenName).as_stubbed_const - check_verification LoadedClassWithOverridenName + class_double(LoadedClassWithOverriddenName).as_stubbed_const + check_verification LoadedClassWithOverriddenName end def check_verification(o) diff --git a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb index 5455fcaa9..e7faf79f2 100644 --- a/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb @@ -167,9 +167,9 @@ module Mocks end end - context "when given a class that has an overriden `#name` method" do + context "when given a class that has an overridden `#name` method" do it "properly verifies" do - o = instance_double(LoadedClassWithOverridenName) + o = instance_double(LoadedClassWithOverriddenName) allow(o).to receive(:defined_instance_method) prevents { allow(o).to receive(:undefined_method) } end diff --git a/spec/support/doubled_classes.rb b/spec/support/doubled_classes.rb index eb2132c0e..ab7d9dc72 100644 --- a/spec/support/doubled_classes.rb +++ b/spec/support/doubled_classes.rb @@ -80,7 +80,7 @@ def defined_private_method end end -class LoadedClassWithOverridenName < LoadedClass +class LoadedClassWithOverriddenName < LoadedClass def self.name "Overriding name is not a good idea but we can't count on users not doing this" end From 45270292c7f9785697b31d63e2ce1b995388954a Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 31 Dec 2017 17:41:46 +1100 Subject: [PATCH 1095/1179] Upgrade yard to 0.9.12, motivated by CVE. --- Gemfile | 2 +- lib/rspec/mocks/proxy.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index cecea0ebe..6ac6478b2 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp end end -gem 'yard', '~> 0.8.7', :require => false +gem 'yard', '~> 0.9.12', :require => false if RUBY_VERSION >= '2' && RUBY_VERSION <= '2.1' # todo upgrade rubocop and run on a recent version e.g. 2.3 or 2.4 diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index d9e6ee240..84d2b4561 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -2,6 +2,7 @@ module RSpec module Mocks # @private class Proxy + # @private SpecificMessage = Struct.new(:object, :message, :args) do def ==(expectation) expectation.orig_object == object && expectation.matches?(message, *args) From 871eb31e3bfe50705ca57e754771aa1e0164f12d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 30 Dec 2017 17:45:43 -0800 Subject: [PATCH 1096/1179] Skip spec that does not apply to Ruby 2.5. Ruby 2.5 has removed the ability to access top-level constants via a confusing nested form (e.g. `MyClass::Hash`), so we no longer need this spec there. For #1192. --- spec/rspec/mocks/mutate_const_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/mutate_const_spec.rb b/spec/rspec/mocks/mutate_const_spec.rb index fe8d4128e..9d80dd736 100644 --- a/spec/rspec/mocks/mutate_const_spec.rb +++ b/spec/rspec/mocks/mutate_const_spec.rb @@ -170,7 +170,7 @@ def change_const_value_to(value) expect(::Hash).to equal(top_level_hash) end - it 'does not affect the ability to access the top-level constant from nested contexts', :silence_warnings do + it 'does not affect the ability to access the top-level constant from nested contexts', :silence_warnings, :if => RUBY_VERSION < '2.5' do top_level_hash = ::Hash hide_const("TestClass::Hash") From 783923d6879a2f9df9fee8ef24cecca6ac21136e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 30 Dec 2017 23:35:27 -0800 Subject: [PATCH 1097/1179] Make spec less brittle. On Ruby 2.5, this spec failed, apparently due to the fact that Method equality has changed on 2.5 slightly. The method instances have always been different but 2.4 and before considered them equivalent. Instead, we can show that the two method objects _behave_ the same, which is what we really care about. Fixes #1192. --- spec/rspec/mocks/and_wrap_original_spec.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/rspec/mocks/and_wrap_original_spec.rb b/spec/rspec/mocks/and_wrap_original_spec.rb index df9cae464..2d4d0a8b9 100644 --- a/spec/rspec/mocks/and_wrap_original_spec.rb +++ b/spec/rspec/mocks/and_wrap_original_spec.rb @@ -26,12 +26,13 @@ def results }.to raise_error NameError end - it "passes in the original method" do - value = nil + it "passes along the original method" do + passed_method = nil original_method = instance.method(:results) - allow_it.to receive(:results).and_wrap_original { |m| value = m } + allow_it.to receive(:results).and_wrap_original { |m| passed_method = m } instance.results - expect(value).to eq original_method + + expect(passed_method.call).to eq(original_method.call) end it "passes along the message arguments" do From efb34da5da7f12d95e5d0d0a1d9956605d9c5a93 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 2 Jan 2018 08:14:15 -0800 Subject: [PATCH 1098/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 10 ++++++---- appveyor.yml | 4 +++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 8 ++------ script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 11926c2e3..e67c11cdd 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. +# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 5640684e9..d42b3e6d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. +# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -8,6 +8,7 @@ cache: directories: - ../bundle before_install: + - gem update --system # https://github.com/travis-ci/travis-ci/issues/8978#issuecomment-354036443 - unset _JAVA_OPTIONS - "script/clone_all_rspec_repos" # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it, the excluded versions are broken on Ruby 2.3 @@ -21,9 +22,10 @@ rvm: - 1.9.3 - 2.0.0 - 2.1 - - 2.2.7 - - 2.3.5 - - 2.4.2 + - 2.2.9 + - 2.3.6 + - 2.4.3 + - 2.5.0 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index 8558b71a0..fec0a4a2c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. +# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -37,3 +37,5 @@ environment: - ruby_version: 200 - ruby_version: 21 - ruby_version: 22 + - ruby_version: 23-x64 + - ruby_version: 24-x64 diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index a8b29cb8f..57a848f7c 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. +# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index da940fb46..1693edaa9 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. +# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -83,11 +83,7 @@ function run_spec_suite_for { pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - if is_mri_192_plus; then - travis_retry eval "RUBYOPT=$RUBYOPT:'--enable rubygems' bundle install $bundle_install_flags" - else - travis_retry eval "bundle install $bundle_install_flags" - fi + travis_retry eval "(unset RUBYOPT; exec bundle install $bundle_install_flags)" run_specs_and_record_done popd else diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 821a1469f..7c41439f6 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. +# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index e2801d910..824e13528 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. +# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 484bc61ce..ed537f315 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2017-11-21T14:21:55+11:00 from the rspec-dev repo. +# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 1a5213850fd826b68709be177ed5b6a1f9f7b2f9 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 1 Jan 2018 09:37:52 +1100 Subject: [PATCH 1099/1179] Upgrade rubocop, motivated by CVE. Default to disabling all new cops, though did leave some obvious linter/performance ones on. Only run rubocop on recent versions, and enable gems when we do so (needed for newest rubocop to work). --- .rubocop.yml | 29 +++++-- .rubocop_rspec_base.yml | 109 +++++++++++++++++++++++-- Gemfile | 6 +- lib/rspec/mocks/error_generator.rb | 4 +- lib/rspec/mocks/message_expectation.rb | 4 +- script/functions.sh | 2 +- 6 files changed, 132 insertions(+), 22 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index d2bde5df3..3e9092c43 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,17 +1,28 @@ -inherit_from: .rubocop_rspec_base.yml +inherit_from: + - .rubocop_rspec_base.yml -# This should go down over time. -Style/ClassLength: +# All these metrics should go down over time. + +Metrics/ClassLength: Max: 279 -# This should go down over time. -Style/CyclomaticComplexity: +Metrics/CyclomaticComplexity: Max: 18 -# This should go down over time. -Style/LineLength: +Metrics/LineLength: Max: 193 -# This should go down over time. -Style/MethodLength: +Metrics/MethodLength: Max: 49 + +Metrics/AbcSize: + Max: 45 + +Metrics/BlockLength: + Max: 45 + +Metrics/ModuleLength: + Max: 209 + +Metrics/PerceivedComplexity: + Max: 19 diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index e67c11cdd..cd28556e5 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -50,9 +50,6 @@ DoubleNegation: EachWithObject: Enabled: false -Encoding: - EnforcedStyle: when_needed - FormatString: EnforcedStyle: percent @@ -73,7 +70,7 @@ MethodLength: Max: 15 # Who cares what we call the argument for binary operator methods? -OpMethod: +BinaryOperatorParameterName: Enabled: false PercentLiteralDelimiters: @@ -121,10 +118,112 @@ StringLiterals: Style/SpecialGlobalVars: Enabled: false -Style/TrailingComma: +Style/TrailingCommaInLiteral: + Enabled: false + +Style/TrailingCommaInArguments: Enabled: false TrivialAccessors: AllowDSLWriters: true AllowPredicates: true ExactNameMatch: true + +Style/ParallelAssignment: + Enabled: false + +Layout/EmptyLineBetweenDefs: + Enabled: false + +Layout/FirstParameterIndentation: + Enabled: false + +Naming/ConstantName: + Enabled: false + +Style/ClassCheck: + Enabled: false + +Style/ConditionalAssignment: + Enabled: false + +Style/EmptyMethod: + Enabled: false + +Style/FormatStringToken: + Enabled: false + +Style/GuardClause: + Enabled: false + +Style/IdenticalConditionalBranches: + Enabled: false + +Style/IfUnlessModifier: + Enabled: false + +Style/IfUnlessModifierOfIfUnless: + Enabled: false + +Style/MethodMissing: + Enabled: false + +Style/MixinUsage: + Enabled: false + +Style/MultipleComparison: + Enabled: false + +Style/MutableConstant: + Enabled: false + +Style/NestedModifier: + Enabled: false + +Style/NestedParenthesizedCalls: + Enabled: false + +Style/NumericPredicate: + Enabled: false + +Style/RedundantParentheses: + Enabled: false + +Style/StringLiteralsInInterpolation: + Enabled: false + +Style/SymbolArray: + Enabled: false + +Style/SymbolProc: + Enabled: false + +Style/YodaCondition: + Enabled: false + +Style/ZeroLengthPredicate: + Enabled: false + +Layout/ClosingParenthesisIndentation: + Enabled: false + +Layout/ExtraSpacing: + Enabled: false + +Layout/MultilineMethodCallBraceLayout: + Enabled: false + +Layout/MultilineMethodCallIndentation: + Enabled: false + +Layout/MultilineOperationIndentation: + Enabled: false + +Layout/SpaceAroundBlockParameters: + Enabled: false + +Layout/SpaceAroundOperators: + Enabled: false + +Layout/SpaceBeforeComma: + Enabled: false diff --git a/Gemfile b/Gemfile index 6ac6478b2..78d863380 100644 --- a/Gemfile +++ b/Gemfile @@ -14,9 +14,9 @@ end gem 'yard', '~> 0.9.12', :require => false -if RUBY_VERSION >= '2' && RUBY_VERSION <= '2.1' - # todo upgrade rubocop and run on a recent version e.g. 2.3 or 2.4 - gem 'rubocop', "~> 0.23.0" +# No need to run rubocop on earlier versions +if RUBY_VERSION >= '2.4' && RUBY_ENGINE == 'ruby' + gem 'rubocop', "~> 0.52.1" end if RUBY_VERSION < '2.0.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) diff --git a/lib/rspec/mocks/error_generator.rb b/lib/rspec/mocks/error_generator.rb index 06ab1a046..9bf0984f3 100644 --- a/lib/rspec/mocks/error_generator.rb +++ b/lib/rspec/mocks/error_generator.rb @@ -72,7 +72,7 @@ def default_error_message(expectation, expected_args, actual_args) "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}".dup end - # rubocop:disable Style/ParameterLists + # rubocop:disable Metrics/ParameterLists # @private def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, args, @@ -81,7 +81,7 @@ def raise_expectation_error(message, expected_received_count, argument_list_matc received_part = received_part_of_expectation_error(actual_received_count, args) __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line, source_id end - # rubocop:enable Style/ParameterLists + # rubocop:enable Metrics/ParameterLists # @private def raise_unimplemented_error(doubled_module, method_name, object) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 160f67346..c6bb3fb5e 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -367,7 +367,7 @@ module ImplementationDetails # @private attr_reader :type - # rubocop:disable Style/ParameterLists + # rubocop:disable Metrics/ParameterLists def initialize(error_generator, expectation_ordering, expected_from, method_double, type=:expectation, opts={}, &implementation_block) @type = type @@ -395,7 +395,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @implementation = Implementation.new self.inner_implementation_action = implementation_block end - # rubocop:enable Style/ParameterLists + # rubocop:enable Metrics/ParameterLists def expected_args @argument_list_matcher.expected_args diff --git a/script/functions.sh b/script/functions.sh index 1693edaa9..cd53f7522 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -180,7 +180,7 @@ function check_documentation_coverage { function check_style_and_lint { echo "bin/rubocop lib" - bin/rubocop lib + eval "(unset RUBYOPT; exec bin/rubocop lib)" } function run_all_spec_suites { From 79dce732f79361ceaacf549c7a8c5cc4fbb0c080 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 1 Jan 2018 09:43:06 +1100 Subject: [PATCH 1100/1179] Performance change suggested by rubocop. --- lib/rspec/mocks/message_expectation.rb | 2 +- lib/rspec/mocks/test_double.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index c6bb3fb5e..5aff13ff8 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -650,7 +650,7 @@ def call(*_args_to_ignore, &block) @error_generator.raise_wrong_arity_error(args, block_signature) end - value = @eval_context ? @eval_context.instance_exec(*args, &block) : block.call(*args) + value = @eval_context ? @eval_context.instance_exec(*args, &block) : yield(*args) end value end diff --git a/lib/rspec/mocks/test_double.rb b/lib/rspec/mocks/test_double.rb index 54ffc92ed..e8bfbeb59 100644 --- a/lib/rspec/mocks/test_double.rb +++ b/lib/rspec/mocks/test_double.rb @@ -44,7 +44,7 @@ def inspect # @private def to_s - inspect.gsub('<', '[').gsub('>', ']') + inspect.tr('<', '[').tr('>', ']') end # @private From fb2b42f81ff988371122bb61ab1f316445ffe696 Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Mon, 1 Jan 2018 09:45:52 +1100 Subject: [PATCH 1101/1179] Move un-private class methods out of private block. Rubocop was pointing out that private didn't have any effect here, which was true. Making them actually private breaks the entire test suite. --- .rubocop_rspec_base.yml | 5 +++++ lib/rspec/mocks/method_reference.rb | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index cd28556e5..8919f5beb 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -227,3 +227,8 @@ Layout/SpaceAroundOperators: Layout/SpaceBeforeComma: Enabled: false + +# This could likely be enabled, but it had a false positive on rspec-mocks +# (suggested change was not behaviour preserving) so I don't trust it. +Performance/HashEachMethods: + Enabled: false diff --git a/lib/rspec/mocks/method_reference.rb b/lib/rspec/mocks/method_reference.rb index fa08e3704..026c2c07d 100644 --- a/lib/rspec/mocks/method_reference.rb +++ b/lib/rspec/mocks/method_reference.rb @@ -64,14 +64,6 @@ def visibility :public end - private - - def original_method - @object_reference.when_loaded do |m| - self.defined? && find_method(m) - end - end - def self.instance_method_visibility_for(klass, method_name) if klass.public_method_defined?(method_name) :public @@ -107,6 +99,14 @@ def self.method_visibility_for(object, method_name) return :public if visible end + + private + + def original_method + @object_reference.when_loaded do |m| + self.defined? && find_method(m) + end + end end # @private From 2756caaca8e6a2313688c6ca73f3a6f5543ea002 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Thu, 15 Feb 2018 19:38:47 +0000 Subject: [PATCH 1102/1179] Use https:// instead of git:// URLs in READMEs See https://github.com/rspec/rspec-core/pull/2518. --- DEV-README.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEV-README.md b/DEV-README.md index cbb78abec..cef861bab 100644 --- a/DEV-README.md +++ b/DEV-README.md @@ -1,6 +1,6 @@ ## Set up the dev environment - git clone git://github.com/rspec/rspec-mocks.git + git clone https://github.com/rspec/rspec-mocks.git cd rspec-mocks gem install bundler bundle install diff --git a/README.md b/README.md index 1af8adf4e..bb8dfe3fc 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ RSpec repos as well. Add the following to your `Gemfile`: ```ruby %w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| - gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => 'master' + gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'master' end ``` ## Contributing From 3183a52b4bef813b4cbeeec39d17785df3bafabc Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Sun, 4 Mar 2018 16:52:14 +0100 Subject: [PATCH 1103/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 86 +++++++++++++++++++++++++++++++++-- .travis.yml | 3 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 89 insertions(+), 12 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 8919f5beb..a029b8760 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,11 +1,11 @@ -# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. +# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects # can customize by inheriting this file and overriding particular settings. AccessModifierIndentation: - EnforcedStyle: outdent + Enabled: false # "Use alias_method instead of alias" # We're fine with `alias`. @@ -95,9 +95,6 @@ PredicateName: Proc: Enabled: false -RedundantReturn: - AllowMultipleReturnValues: true - # Exceptions should be rescued with `Support::AllExceptionsExceptOnesWeMustNotRescue` RescueException: Enabled: true @@ -228,7 +225,86 @@ Layout/SpaceAroundOperators: Layout/SpaceBeforeComma: Enabled: false +Style/BlockDelimiters: + Enabled: false + +Style/EmptyCaseCondition: + Enabled: false + +Style/MultilineIfModifier: + Enabled: false + +Style/RescueStandardError: + Enabled: false + +Style/StderrPuts: + Enabled: false + +Style/TernaryParentheses: + Enabled: false + # This could likely be enabled, but it had a false positive on rspec-mocks # (suggested change was not behaviour preserving) so I don't trust it. Performance/HashEachMethods: Enabled: false + +Naming/HeredocDelimiterNaming: + Enabled: false + +Layout/EmptyLineAfterMagicComment: + Enabled: false + +Layout/IndentArray: + Enabled: false + +Layout/IndentAssignment: + Enabled: false + +Layout/IndentHeredoc: + Enabled: false + +Layout/SpaceInsidePercentLiteralDelimiters: + Enabled: false + +Style/EmptyElse: + Enabled: false + +Style/IfInsideElse: + Enabled: false + +Style/RedundantReturn: + Enabled: false + +Style/StructInheritance: + Enabled: false + +Naming/VariableNumber: + Enabled: false + +Layout/SpaceInsideStringInterpolation: + Enabled: false + +Style/DateTime: + Enabled: false + +Style/ParenthesesAroundCondition: + Enabled: false + +Layout/EmptyLinesAroundBlockBody: + Enabled: false + +Lint/ImplicitStringConcatenation: + Enabled: false + +Lint/NestedMethodDefinition: + Enabled: false + +Style/RegexpLiteral: + Enabled: false + +Style/TrailingUnderscoreVariable: + Enabled: false + +Layout/EmptyLinesAroundAccessModifier: + Enabled: false + diff --git a/.travis.yml b/.travis.yml index d42b3e6d2..be8b94a87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. +# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -42,6 +42,7 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx-3 + - rvm: ruby-2.6.0-preview1 fast_finish: true branches: only: diff --git a/appveyor.yml b/appveyor.yml index fec0a4a2c..77f9cc46a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. +# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 57a848f7c..73ad938b0 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. +# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index cd53f7522..6d88c0a83 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. +# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 7c41439f6..bf8efaeff 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. +# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 824e13528..4cd25a954 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. +# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index ed537f315..5cb38c36f 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-01-02T08:14:15-08:00 from the rspec-dev repo. +# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 828e5413b8847f085c87d04ce32ef4e8bdae3fc4 Mon Sep 17 00:00:00 2001 From: Alexander Panasyuk Date: Mon, 5 Mar 2018 23:27:52 +0700 Subject: [PATCH 1104/1179] Fixed mistype in declaration and usage of RSpec::Mocks::AnyInstance::StubChain::InvocationOrder constant --- lib/rspec/mocks/any_instance/stub_chain.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/any_instance/stub_chain.rb b/lib/rspec/mocks/any_instance/stub_chain.rb index 85e767828..c4c0ab748 100644 --- a/lib/rspec/mocks/any_instance/stub_chain.rb +++ b/lib/rspec/mocks/any_instance/stub_chain.rb @@ -25,7 +25,7 @@ def create_message_expectation_on(instance) stub end - InovcationOrder = + InvocationOrder = { :and_return => [:with, nil], :and_raise => [:with, nil], @@ -38,7 +38,7 @@ def create_message_expectation_on(instance) EmptyInvocationOrder = {}.freeze def invocation_order - InovcationOrder + InvocationOrder end def verify_invocation_order(rspec_method_name, *_args, &_block) From b5bdc56b474e24b426fe409f62851c2912d9ccf4 Mon Sep 17 00:00:00 2001 From: Sergiy Yarinovskiy Date: Mon, 19 Mar 2018 22:48:02 +0200 Subject: [PATCH 1105/1179] ignore counts for negative expectations --- .rubocop.yml | 2 +- lib/rspec/mocks/message_expectation.rb | 1 + spec/rspec/mocks/at_least_spec.rb | 8 ++++++++ spec/rspec/mocks/at_most_spec.rb | 8 ++++++++ spec/rspec/mocks/once_counts_spec.rb | 8 ++++++++ spec/rspec/mocks/thrice_counts_spec.rb | 8 ++++++++ spec/rspec/mocks/twice_counts_spec.rb | 8 ++++++++ 7 files changed, 42 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 3e9092c43..561e4a0b0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,7 +22,7 @@ Metrics/BlockLength: Max: 45 Metrics/ModuleLength: - Max: 209 + Max: 210 Metrics/PerceivedComplexity: Max: 19 diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 5aff13ff8..d615867d2 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -581,6 +581,7 @@ def raise_already_invoked_error_if_necessary(calling_customization) end def set_expected_received_count(relativity, n) + raise "`count` is not supported with negative message expectations" if negative? @at_least = (relativity == :at_least) @at_most = (relativity == :at_most) @exactly = (relativity == :exactly) diff --git a/spec/rspec/mocks/at_least_spec.rb b/spec/rspec/mocks/at_least_spec.rb index e65dc1298..9e47b33d1 100644 --- a/spec/rspec/mocks/at_least_spec.rb +++ b/spec/rspec/mocks/at_least_spec.rb @@ -142,6 +142,14 @@ module Mocks expect(@double.do_something).to eq 'bar' expect(@double.do_something).to eq 'bar' end + + context "when called with negative expectation" do + it "raises an error" do + expect { + expect(@double).not_to receive(:do_something).at_least(:thrice) + }.to raise_error(/`count` is not supported with negative message expectations/) + end + end end end end diff --git a/spec/rspec/mocks/at_most_spec.rb b/spec/rspec/mocks/at_most_spec.rb index cb5fe7271..25a2a1ec5 100644 --- a/spec/rspec/mocks/at_most_spec.rb +++ b/spec/rspec/mocks/at_most_spec.rb @@ -108,6 +108,14 @@ module Mocks @double.do_something end end + + context "when called with negative expectation" do + it "raises an error" do + expect { + expect(@double).not_to receive(:do_something).at_most(:thrice) + }.to raise_error(/`count` is not supported with negative message expectations/) + end + end end end end diff --git a/spec/rspec/mocks/once_counts_spec.rb b/spec/rspec/mocks/once_counts_spec.rb index d44d276d0..e65231fc7 100644 --- a/spec/rspec/mocks/once_counts_spec.rb +++ b/spec/rspec/mocks/once_counts_spec.rb @@ -68,6 +68,14 @@ module Mocks }.to fail_with(a_string_including("expected: 1 time", "received: 2 times")) end end + + context "when called with negative expectation" do + it "raises an error" do + expect { + expect(@double).not_to receive(:do_something).once + }.to raise_error(/`count` is not supported with negative message expectations/) + end + end end end end diff --git a/spec/rspec/mocks/thrice_counts_spec.rb b/spec/rspec/mocks/thrice_counts_spec.rb index 6a8106fdc..490f7448a 100644 --- a/spec/rspec/mocks/thrice_counts_spec.rb +++ b/spec/rspec/mocks/thrice_counts_spec.rb @@ -67,6 +67,14 @@ module Mocks }.to fail reset @double end + + context "when called with negative expectation" do + it "raises an error" do + expect { + expect(@double).not_to receive(:do_something).thrice + }.to raise_error(/`count` is not supported with negative message expectations/) + end + end end end end diff --git a/spec/rspec/mocks/twice_counts_spec.rb b/spec/rspec/mocks/twice_counts_spec.rb index 917f98ab2..8cb75ce14 100644 --- a/spec/rspec/mocks/twice_counts_spec.rb +++ b/spec/rspec/mocks/twice_counts_spec.rb @@ -84,6 +84,14 @@ module Mocks }.to fail_with(a_string_including("expected: 2 times", "received: 3 times")) end end + + context "when called with negative expectation" do + it "raises an error" do + expect { + expect(@double).not_to receive(:do_something).twice + }.to raise_error(/`count` is not supported with negative message expectations/) + end + end end end end From 1e1fbbfe34543775dec8e8aa0facd92e032cb2bc Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 26 Mar 2018 11:05:51 +1100 Subject: [PATCH 1106/1179] Changelog for #1212 --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index a5475d458..41d0b62a8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,11 @@ ### 3.8 Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...master) +Bug Fixes: + +* Issue error when encountering invalid "counted" negative message expectations. + (Sergiy Yarinovskiy, #1212) + ### 3.7.0 / 2017-10-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0...v3.7.0) From 14ca6fd98ca530003f15b995c8545763520f60d3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 3 Apr 2018 20:02:52 +1000 Subject: [PATCH 1107/1179] Pin ffi on 1.8.7 --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 78d863380..22fa0843e 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,8 @@ end if RUBY_VERSION < '2.0.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) gem 'ffi', '< 1.9.15' # allow ffi to be installed on older rubies on windows +elsif RUBY_VERSION < '1.9' + gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19 end ### deps for rdoc.info From 6cc9319527bed8d34e30077995e327d9a0b7dc51 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 5 Apr 2018 18:41:16 +1000 Subject: [PATCH 1108/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 3 +-- .travis.yml | 14 ++++++-------- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index a029b8760..db7028464 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. +# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects @@ -307,4 +307,3 @@ Style/TrailingUnderscoreVariable: Layout/EmptyLinesAroundAccessModifier: Enabled: false - diff --git a/.travis.yml b/.travis.yml index be8b94a87..ebb749925 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. +# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -9,11 +9,9 @@ cache: - ../bundle before_install: - gem update --system # https://github.com/travis-ci/travis-ci/issues/8978#issuecomment-354036443 + - gem install bundler - unset _JAVA_OPTIONS - "script/clone_all_rspec_repos" - # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it, the excluded versions are broken on Ruby 2.3 - - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler --version "1.11.2"; fi - - alias bundle="bundle _1.11.2_" bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: @@ -22,10 +20,10 @@ rvm: - 1.9.3 - 2.0.0 - 2.1 - - 2.2.9 - - 2.3.6 - - 2.4.3 - - 2.5.0 + - 2.2.10 + - 2.3.7 + - 2.4.4 + - 2.5.1 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index 77f9cc46a..469175b41 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. +# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 73ad938b0..cbbef3743 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. +# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 6d88c0a83..7a769e257 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. +# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index bf8efaeff..4d592e273 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. +# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 4cd25a954..4b42ed37d 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. +# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 5cb38c36f..7473ae719 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-03-04T16:52:14+01:00 from the rspec-dev repo. +# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 0cb5925186db03db540ac92ad6bb0a7fd524ceab Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Tue, 17 Apr 2018 19:41:57 +0200 Subject: [PATCH 1109/1179] Updated common markdown files (from rspec-dev) [ci skip] --- BUILD_DETAIL.md | 2 +- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 11 +++++++++-- DEVELOPMENT.md | 2 +- ISSUE_TEMPLATE.md | 24 ++++++++++++++++++++++++ REPORT_TEMPLATE.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 ISSUE_TEMPLATE.md create mode 100644 REPORT_TEMPLATE.md diff --git a/BUILD_DETAIL.md b/BUILD_DETAIL.md index 04bb73e05..39271c8ea 100644 --- a/BUILD_DETAIL.md +++ b/BUILD_DETAIL.md @@ -1,5 +1,5 @@ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 17807af00..9b73017c4 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,5 +1,5 @@ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 656612a26..4dd7a1e13 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ @@ -11,7 +11,7 @@ We welcome contributions from *everyone*. While contributing, please follow the If you'd like to help make RSpec better, here are some ways you can contribute: - by running RSpec HEAD to help us catch bugs before new releases - - by [reporting bugs you encounter](https://github.com/rspec/rspec-mocks/issues/new) + - by [reporting bugs you encounter](https://github.com/rspec/rspec-mocks/issues/new) with [report template](#report-template) - by [suggesting new features](https://github.com/rspec/rspec-mocks/issues/new) - by improving RSpec's [Relish](https://relishapp.com/rspec) or [API](http://rspec.info/documentation/) documentation - by improving [RSpec's website](http://rspec.info/) ([source](https://github.com/rspec/rspec.github.io)) @@ -30,6 +30,13 @@ These issue are ones that we be believe are best suited for new contributors to get started with. They represent a meaningful contribution to the project that should not be too hard to pull off. +## Report template + +Having a way to reproduce your issue will be very helpful for others to help confirm, +investigate and ultimately fix your issue. You can do this by providing an executable +test case. To make this process easier, we have prepared one basic +[bug report templates](REPORT_TEMPLATE.md) for you to use as a starting point. + ## Maintenance branches Maintenance branches are how we manage the different supported point releases diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 3903087a4..d1b305a27 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,5 +1,5 @@ diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..d3bdbecb9 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,24 @@ +### Subject of the issue + + +### Your environment +* Ruby version: +* rspec-mocks version: + +### Steps to reproduce + + +### Expected behavior + + +### Actual behavior + diff --git a/REPORT_TEMPLATE.md b/REPORT_TEMPLATE.md new file mode 100644 index 000000000..eff8136ce --- /dev/null +++ b/REPORT_TEMPLATE.md @@ -0,0 +1,43 @@ + + +# Report template + +```ruby +# frozen_string_literal: true + +begin + require "bundler/inline" +rescue LoadError => e + $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler" + raise e +end + +gemfile(true) do + source "https://rubygems.org" + + gem "rspec", "3.7.0" # Activate the gem and version you are reporting the issue against. +end + +puts "Ruby version is: #{RUBY_VERSION}" +require 'rspec/autorun' + +RSpec.describe 'additions' do + it 'returns 2' do + expect(1 + 1).to eq(2) + end + + it 'returns 1' do + expect(3 - 1).to eq(-1) + end +end +``` + +Simply copy the content of the appropriate template into a `.rb` file on your computer +and make the necessary changes to demonstrate the issue. You can execute it by running +`ruby rspec_report.rb` in your terminal. + +You can then share your executable test case as a [gist](https://gist.github.com), or +simply paste the content into the issue description. From df6bf3d1aa50ed5070a450bd7ecace7a9dadf0b3 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 13 May 2018 22:14:18 +1000 Subject: [PATCH 1110/1179] Updated ArgumentMatcher documentation. (#1222) Added `having_attributes` to ArgumentMatcher documentation. --- .../matching_arguments.feature | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/features/setting_constraints/matching_arguments.feature b/features/setting_constraints/matching_arguments.feature index 219302fc1..6c1ac4694 100644 --- a/features/setting_constraints/matching_arguments.feature +++ b/features/setting_constraints/matching_arguments.feature @@ -4,22 +4,23 @@ Feature: Matching arguments will only be satisfied when called with matching arguments. A canned response for an [allowed message](../basics/allowing-messages) will only be used when the arguments match. - | To match... | ...use an expression like: | ...which matches calls like: | - | --------------------------------------------------- | ------------------------------- | ------------------------------------- | - | Literal arguments | `with(1, true)` | `foo(1, true)` | - | Anything that supports case equality (`===`) | `with(/bar/)` | `foo("barn")` | - | Any list of args | `with(any_args)` | `foo()`
`foo(1)`
`foo(:bar, 2)` | - | Any sublist of args (like an arg splat) | `with(1, any_args)` | `foo(1)`
`foo(1, :bar, :bazz)` | - | An empty list of args | `with(no_args)` | `foo()` | - | Anything for a given positional arg | `with(3, anything)` | `foo(3, nil)`
`foo(3, :bar)` | - | Against an interface | `with(duck_type(:each))` | `foo([])` | - | A boolean | `with(3, boolean)` | `foo(3, true)`
`foo(3, false)` | - | A subset of a hash | `with(hash_including(:a => 1))` | `foo(:a => 1, :b => 2)` | - | An excluded subset of a hash | `with(hash_excluding(:a => 1))` | `foo(:b => 2)` | - | A subset of an array | `with(array_including(:a, :b))` | `foo([:a, :b, :c])` | - | An instance of a specific class | `with(instance_of(Integer))` | `foo(3)` | - | An object with a given module in its ancestors list | `with(kind_of(Numeric))` | `foo(3)` | - | Any RSpec matcher | `with()` | `foo()` | + | To match... | ...use an expression like: | ...which matches calls like: | + | --------------------------------------------------- | --------------------------------- | ------------------------------------- | + | Literal arguments | `with(1, true)` | `foo(1, true)` | + | Anything that supports case equality (`===`) | `with(/bar/)` | `foo("barn")` | + | Any list of args | `with(any_args)` | `foo()`
`foo(1)`
`foo(:bar, 2)` | + | Any sublist of args (like an arg splat) | `with(1, any_args)` | `foo(1)`
`foo(1, :bar, :bazz)` | + | An empty list of args | `with(no_args)` | `foo()` | + | Anything for a given positional arg | `with(3, anything)` | `foo(3, nil)`
`foo(3, :bar)` | + | Against an interface | `with(duck_type(:each))` | `foo([])` | + | A boolean | `with(3, boolean)` | `foo(3, true)`
`foo(3, false)` | + | A subset of a hash | `with(hash_including(:a => 1))` | `foo(:a => 1, :b => 2)` | + | An excluded subset of a hash | `with(hash_excluding(:a => 1))` | `foo(:b => 2)` | + | A subset of an array | `with(array_including(:a, :b))` | `foo([:a, :b, :c])` | + | An instance of a specific class | `with(instance_of(Integer))` | `foo(3)` | + | An object with a given module in its ancestors list | `with(kind_of(Numeric))` | `foo(3)` | + | An object with matching attributes | `with(having_attributes(:a => 1))` | `foo(:a => 1, :b => 2)` | + | Any RSpec matcher | `with()` | `foo()` | Scenario: Basic example Given a file named "basic_example_spec.rb" with: From 449972fd3d7db091888caff34f0a0304712cbee3 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 19 Jun 2018 16:12:15 +1000 Subject: [PATCH 1111/1179] Support temporarily supressing any isntance expectations (#1228) --- lib/rspec/mocks/any_instance/recorder.rb | 2 +- spec/rspec/mocks/partial_double_spec.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 38fc16f23..0db97af31 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -242,7 +242,7 @@ def public_protected_or_private_method_defined?(method_name) def observe!(method_name) allow_no_prepended_module_definition_of(method_name) - if RSpec::Mocks.configuration.verify_partial_doubles? + if RSpec::Mocks.configuration.verify_partial_doubles? && !Mocks.configuration.temporarily_suppress_partial_double_verification unless public_protected_or_private_method_defined?(method_name) AnyInstance.error_generator.raise_does_not_implement_error(@klass, method_name) end diff --git a/spec/rspec/mocks/partial_double_spec.rb b/spec/rspec/mocks/partial_double_spec.rb index aea06ca2f..c9c0e4a5f 100644 --- a/spec/rspec/mocks/partial_double_spec.rb +++ b/spec/rspec/mocks/partial_double_spec.rb @@ -380,8 +380,10 @@ def defined_private_method it 'can be temporarily supressed' do without_partial_double_verification do expect(object).to receive(:fictitious_method) { 'works' } + expect_any_instance_of(klass).to receive(:other_fictitious_method) { 'works' } end expect(object.fictitious_method).to eq 'works' + expect(object.other_fictitious_method).to eq 'works' expect { expect(object).to receive(:another_fictitious_method) { 'works' } From 7dc2a431101172d31b780458eb168e05d20fb3aa Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 19 Jun 2018 16:42:11 +1000 Subject: [PATCH 1112/1179] Changelog for #1228 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 41d0b62a8..67ecf483c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Bug Fixes: * Issue error when encountering invalid "counted" negative message expectations. (Sergiy Yarinovskiy, #1212) +* Ensure `allow_any_instance_of` and `expect_any_instance_of` can be temporarily + supressed. (Jon Rowe, #1228) ### 3.7.0 / 2017-10-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0...v3.7.0) From 51495b5f554fccdb147ed7d49d8a1c0dc0fc3d5e Mon Sep 17 00:00:00 2001 From: Maxim Krizhanovsky Date: Mon, 25 Jun 2018 18:25:05 +0300 Subject: [PATCH 1113/1179] Improve error message for negative any_instance spy expectations --- lib/rspec/mocks/matchers/have_received.rb | 4 ++++ spec/rspec/mocks/matchers/have_received_spec.rb | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index caa7e20b9..5b9aa8f10 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -76,6 +76,10 @@ def setup_any_instance_expectation(_subject, &_block) disallow("expect_any_instance_of") end + def setup_any_instance_negative_expectation(_subject, &_block) + disallow("expect_any_instance_of") + end + private def disallow(type, reason="") diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index f63e73bb6..6cedd0baa 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -635,6 +635,14 @@ module Mocks end end + describe "expect_any_instance_of(...).not_to have_received" do + it "fails because we dont want to support it" do + _expect { + expect_any_instance_of(double).to have_received(:some_method) + }.to fail_with("Using expect_any_instance_of(...) with the `have_received` matcher is not supported.") + end + end + def double_with_met_expectation(method_name, *args) double = double_with_unmet_expectation(method_name) meet_expectation(double, method_name, *args) From 64a4761e33fb05b7b103c007b273fb6b8ab7a651 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 27 Jun 2018 08:07:21 +0100 Subject: [PATCH 1114/1179] Changelog for #1231 --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 67ecf483c..6967f09c7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,9 @@ Bug Fixes: (Sergiy Yarinovskiy, #1212) * Ensure `allow_any_instance_of` and `expect_any_instance_of` can be temporarily supressed. (Jon Rowe, #1228) +* Ensure `expect_any_instance_of(double).to_not have_received(:some_method)` + fails gracefully (as its not supported) rather than issuing a `NoMethodError`. + (Maxim Krizhanovsky, #1231) ### 3.7.0 / 2017-10-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0...v3.7.0) From 844a69a26327846384ba19afbf97d5c0eb25c8eb Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 27 Jun 2018 08:05:56 +0100 Subject: [PATCH 1115/1179] Fix spec to do what title says --- spec/rspec/mocks/matchers/have_received_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 6cedd0baa..01878d5e0 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -638,7 +638,7 @@ module Mocks describe "expect_any_instance_of(...).not_to have_received" do it "fails because we dont want to support it" do _expect { - expect_any_instance_of(double).to have_received(:some_method) + expect_any_instance_of(double).not_to have_received(:some_method) }.to fail_with("Using expect_any_instance_of(...) with the `have_received` matcher is not supported.") end end From 5b09361a12f5abc8e89f6f16a14d854bda378ead Mon Sep 17 00:00:00 2001 From: Maxim Krizhanovsky Date: Wed, 27 Jun 2018 17:11:16 +0300 Subject: [PATCH 1116/1179] Remove duplicated examples from specs (#1233) * Remove duplicated specs * Improve spec descriptions --- spec/rspec/mocks/any_instance_spec.rb | 8 -------- spec/rspec/mocks/array_including_matcher_spec.rb | 4 ++++ spec/rspec/mocks/null_object_double_spec.rb | 2 +- spec/rspec/mocks/verifying_doubles/object_double_spec.rb | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index c269c142d..4d5111d27 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -296,14 +296,6 @@ def private_method; :private_method_return_value; end expect(klass.new('org').a_method).to eq 'org' expect(klass.new.a_method(:arg)).to eq 'value' end - - it 'can combine and_call_original, with, and_return (old syntax)' do - allow_any_instance_of(klass).to receive(:a_method).and_call_original - allow_any_instance_of(klass).to receive(:a_method).with(:arg).and_return('value') - - expect(klass.new('org').a_method).to eq 'org' - expect(klass.new.a_method(:arg)).to eq 'value' - end end context "with #and_raise" do diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb index f9a4495cf..4f8c03afd 100644 --- a/spec/rspec/mocks/array_including_matcher_spec.rb +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -14,6 +14,10 @@ module ArgumentMatchers context "passing" do it "matches the same array" do + expect(array_including([1, 2, 3])).to be === [1, 2, 3] + end + + it "matches the same array, specified without square brackets" do expect(array_including(1, 2, 3)).to be === [1, 2, 3] end diff --git a/spec/rspec/mocks/null_object_double_spec.rb b/spec/rspec/mocks/null_object_double_spec.rb index 6c321e5c0..2b62ef45f 100644 --- a/spec/rspec/mocks/null_object_double_spec.rb +++ b/spec/rspec/mocks/null_object_double_spec.rb @@ -76,7 +76,7 @@ module Mocks expect(val).to equal(@double) end - it 'allows unexpected message sends using `send`' do + it 'allows unexpected message sends using `__send__`' do val = @double.__send__(:foo).__send__(:bar) expect(val).to equal(@double) end diff --git a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb index 7d16ff2c4..2e23572c0 100644 --- a/spec/rspec/mocks/verifying_doubles/object_double_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/object_double_spec.rb @@ -126,7 +126,7 @@ module Mocks allow(o).to receive(:undefined_method) end - it "treats it as being unloaded for `object_double('ConstName')`" do + it "treats it as being unloaded for `object_double(ConstName)`" do o = object_double(LoadedClass::NOINSTANCE) allow(o).to receive(:undefined_method) end From 7ec25fec7ed7a79e35d5567136f1f8b5daf60c7a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 4 Aug 2018 12:42:32 -0700 Subject: [PATCH 1117/1179] Update changelog for 3.8 release. [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6967f09c7..a7f604ab0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.8 Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...master) +### 3.8.0 / 2018-08-04 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0) Bug Fixes: From 48527c59e155e75587c2fd2af8b30bc42b17c7c5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 4 Aug 2018 12:45:29 -0700 Subject: [PATCH 1118/1179] Release 3.8.0. --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 41431158a..550f55503 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.8.pre' + STRING = '3.8.0' end end end From 8ee8d692885ea2d4713c01d0ee6ba3ef2a3251c9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 4 Aug 2018 13:05:24 -0700 Subject: [PATCH 1119/1179] Bump version to 3.9.0.pre. --- lib/rspec/mocks/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index 550f55503..a97d3cc5e 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.8.0' + STRING = '3.9.0.pre' end end end From 1900c5ae858287eaadf70d59b0ed6e03e61653d6 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 21 Sep 2018 09:13:43 +0100 Subject: [PATCH 1120/1179] Updated travis build scripts (from rspec-dev) (#1239) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 3 +-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index db7028464..b9328aaf1 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. +# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index ebb749925..372822db5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. +# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 469175b41..ffeb3ce94 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. +# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -33,7 +33,6 @@ test_script: environment: matrix: - - ruby_version: 193 - ruby_version: 200 - ruby_version: 21 - ruby_version: 22 diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index cbbef3743..73724f1b1 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. +# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 7a769e257..c56b2394b 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. +# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 4d592e273..81c2eda9a 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. +# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 4b42ed37d..9944faaa2 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. +# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 7473ae719..5db54d59c 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-04-05T18:41:16+10:00 from the rspec-dev repo. +# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 24442456174015b6ab7a786e78abbf894d0bbc19 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 16 Oct 2018 21:12:13 +0100 Subject: [PATCH 1121/1179] Improve threadsafety of message expectations Synchronise writes to `MessageExpectation::ImplementationDetail`'s `@actual_received_count` in order to prevent overlapping writes from being ignored. Synchronise all access to `Proxy`'s `@messages_received` array, since mutation while iterating over the array in Java can cause `ConcurrencyError`s. --- lib/rspec/mocks/message_expectation.rb | 13 ++++- lib/rspec/mocks/proxy.rb | 48 +++++++++++++------ .../stubbed_message_expectations_spec.rb | 17 +++++++ 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index d615867d2..90e0a550c 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -1,3 +1,5 @@ +RSpec::Support.require_rspec_support 'mutex' + module RSpec module Mocks # A message expectation that only allows concrete return values to be set @@ -357,6 +359,7 @@ def to_s # some collaborators it delegates to for this stuff but for now this was # the simplest way to split the public from private stuff to make it # easier to publish the docs for the APIs we want published. + # rubocop:disable Metrics/ModuleLength module ImplementationDetails attr_accessor :error_generator, :implementation attr_reader :message @@ -378,6 +381,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub @orig_object = @method_double.object @message = @method_double.method_name @actual_received_count = 0 + @actual_received_count_write_mutex = Support::Mutex.new @expected_received_count = type == :expectation ? 1 : :any @argument_list_matcher = ArgumentListMatcher::MATCH_ALL @order_group = expectation_ordering @@ -536,7 +540,9 @@ def actual_received_count_matters? end def increase_actual_received_count! - @actual_received_count += 1 + @actual_received_count_write_mutex.synchronize do + @actual_received_count += 1 + end end private @@ -567,7 +573,9 @@ def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, parent_stub.invoke(nil, *args, &block) end ensure - @actual_received_count += increment + @actual_received_count_write_mutex.synchronize do + @actual_received_count += increment + end end def has_been_invoked? @@ -626,6 +634,7 @@ def wrap_original(method_name, &block) nil end end + # rubocop:enable Metrics/ModuleLength include ImplementationDetails end diff --git a/lib/rspec/mocks/proxy.rb b/lib/rspec/mocks/proxy.rb index 84d2b4561..6d3583c5b 100644 --- a/lib/rspec/mocks/proxy.rb +++ b/lib/rspec/mocks/proxy.rb @@ -9,6 +9,11 @@ def ==(expectation) end end + unless defined?(Mutex) + Support.require_rspec_support 'mutex' + Mutex = Support::Mutex + end + # @private def ensure_implemented(*_args) # noop for basic proxies, see VerifyingProxy for behaviour. @@ -20,6 +25,7 @@ def initialize(object, order_group, options={}) @order_group = order_group @error_generator = ErrorGenerator.new(object) @messages_received = [] + @messages_received_mutex = Mutex.new @options = options @null_object = false @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) } @@ -90,27 +96,31 @@ def replay_received_message_on(expectation, &block) @error_generator.raise_expectation_on_unstubbed_method(expected_method_name) end - @messages_received.each do |(actual_method_name, args, received_block)| - next unless expectation.matches?(actual_method_name, *args) + @messages_received_mutex.synchronize do + @messages_received.each do |(actual_method_name, args, received_block)| + next unless expectation.matches?(actual_method_name, *args) - expectation.safe_invoke(nil) - block.call(*args, &received_block) if block + expectation.safe_invoke(nil) + block.call(*args, &received_block) if block + end end end # @private def check_for_unexpected_arguments(expectation) - return if @messages_received.empty? + @messages_received_mutex.synchronize do + return if @messages_received.empty? - return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) } + return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) } - name_but_not_args, others = @messages_received.partition do |(method_name, args, _)| - expectation.matches_name_but_not_args(method_name, *args) - end + name_but_not_args, others = @messages_received.partition do |(method_name, args, _)| + expectation.matches_name_but_not_args(method_name, *args) + end - return if name_but_not_args.empty? && !others.empty? + return if name_but_not_args.empty? && !others.empty? - expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] }) + expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] }) + end end # @private @@ -141,17 +151,23 @@ def verify # @private def reset - @messages_received.clear + @messages_received_mutex.synchronize do + @messages_received.clear + end end # @private def received_message?(method_name, *args, &block) - @messages_received.any? { |array| array == [method_name, args, block] } + @messages_received_mutex.synchronize do + @messages_received.any? { |array| array == [method_name, args, block] } + end end # @private def messages_arg_list - @messages_received.map { |_, args, _| args } + @messages_received_mutex.synchronize do + @messages_received.map { |_, args, _| args } + end end # @private @@ -162,7 +178,9 @@ def has_negative_expectation?(message) # @private def record_message_received(message, *args, &block) @order_group.invoked SpecificMessage.new(object, message, args) - @messages_received << [message, args, block] + @messages_received_mutex.synchronize do + @messages_received << [message, args, block] + end end # @private diff --git a/spec/rspec/mocks/stubbed_message_expectations_spec.rb b/spec/rspec/mocks/stubbed_message_expectations_spec.rb index 2181133ee..6e55adea8 100644 --- a/spec/rspec/mocks/stubbed_message_expectations_spec.rb +++ b/spec/rspec/mocks/stubbed_message_expectations_spec.rb @@ -23,6 +23,23 @@ reset dbl end + it 'handles concurrent validation of expectations' do + dbl = double('double', :foo => true) + concurrency = 4 + repetition = 10 + expect(dbl).to receive(:foo).with(anything).exactly(concurrency * repetition).times + + concurrency.times.map do |thread| + Thread.new do + repetition.times do |index| + dbl.foo("#{thread}-#{index}") + end + end + end.map(&:join) + + verify dbl + end + it 'indicates the site of expectation in the stacktrace when outputing arguments of similar calls' do dbl = double('double', :foo => true) expect(dbl).to receive(:foo).with('first'); line = __LINE__ From 7819046bbd9459d80bcdc626f087f5ad6be745cf Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 16 Oct 2018 21:15:57 +0100 Subject: [PATCH 1122/1179] Change log for #1236 and #1244 --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index a7f604ab0..94267b88a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.0...master) + +Enhancements: + +* Improve thread safety of message expectations by using Mutex to prevent + deadlocking errors. (Ry Biesemeyer, #1236) + ### 3.8.0 / 2018-08-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0) From 675ab723d671a720855bf0bbdf32aa908611d0a2 Mon Sep 17 00:00:00 2001 From: Orien Madgwick <_@orien.io> Date: Sat, 20 Oct 2018 18:41:19 +1100 Subject: [PATCH 1123/1179] Add project metadata to the gemspec (#1245) --- rspec-mocks.gemspec | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 0e9cdc0eb..c752d7dc5 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -13,6 +13,14 @@ Gem::Specification.new do |s| s.summary = "rspec-mocks-#{RSpec::Mocks::Version::STRING}" s.description = "RSpec's 'test double' framework, with support for stubbing and mocking" + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/rspec/rspec-mocks/issues', + 'changelog_uri' => "https://github.com/rspec/rspec-mocks/blob/v#{s.version}/Changelog.md", + 'documentation_uri' => 'https://rspec.info/documentation/', + 'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/rspec', + 'source_code_uri' => 'https://github.com/rspec/rspec-mocks', + } + s.files = `git ls-files -- lib/*`.split("\n") s.files += %w[README.md LICENSE.md Changelog.md .yardopts .document] s.test_files = [] From 453d1a0338a62242a3730101f21a7a4a0d1fd8b4 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 13 Nov 2018 12:46:49 +0000 Subject: [PATCH 1124/1179] Prevent Github complaining about FFI (#1247) --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 22fa0843e..a8b61b4fc 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,8 @@ if RUBY_VERSION < '2.0.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|min gem 'ffi', '< 1.9.15' # allow ffi to be installed on older rubies on windows elsif RUBY_VERSION < '1.9' gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19 +else + gem 'ffi', '~> 1.9.25' end ### deps for rdoc.info From 3977f26b09afa8c7407e5b309508c5202a852c91 Mon Sep 17 00:00:00 2001 From: Juanito Fatas Date: Wed, 14 Nov 2018 20:38:30 +0900 Subject: [PATCH 1125/1179] Update terminology in BUILD_DETAIL.md (#1246) --- BUILD_DETAIL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD_DETAIL.md b/BUILD_DETAIL.md index 39271c8ea..6a4fa7dd1 100644 --- a/BUILD_DETAIL.md +++ b/BUILD_DETAIL.md @@ -40,7 +40,7 @@ The spec suite performs a couple extra checks that are worth noting: available for use in any context, we want to minimize how many bits of the standard library we load and use. Otherwise, RSpec's use of part of the standard library could mask a problem where a gem author forgets to load a part of the standard library they - rely on. The spec suite contains a spec that defines a whitelist of allowed loaded + rely on. The spec suite contains a spec that defines a list of allowed loaded stdlibs. In addition, we use [SimpleCov](https://github.com/colszowka/simplecov) From 061c299537fae241ecabc1a4e96a17051c36eb67 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Sat, 17 Nov 2018 09:39:20 +0100 Subject: [PATCH 1126/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 8 ++++---- appveyor.yml | 3 ++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index b9328aaf1..77b11fba1 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. +# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 372822db5..d9b722be8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. +# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -21,9 +21,9 @@ rvm: - 2.0.0 - 2.1 - 2.2.10 - - 2.3.7 - - 2.4.4 - - 2.5.1 + - 2.3.8 + - 2.4.5 + - 2.5.3 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index ffeb3ce94..b05934c5a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. +# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -38,3 +38,4 @@ environment: - ruby_version: 22 - ruby_version: 23-x64 - ruby_version: 24-x64 + - ruby_version: 25-x64 diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 73724f1b1..925ca90d3 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. +# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index c56b2394b..8ef5c60df 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. +# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 81c2eda9a..95828a55a 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. +# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 9944faaa2..aa99cdade 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. +# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 5db54d59c..7c47745b9 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-09-19T14:39:26+01:00 from the rspec-dev repo. +# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From ac569f55087f2daa0e86f5fd42c822861ddf0460 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 21 Dec 2018 21:31:37 +0000 Subject: [PATCH 1127/1179] Updated travis build scripts (from rspec-dev) (#1252) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 5 +++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 77b11fba1..ef31eba07 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. +# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index d9b722be8..18e49d938 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. +# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -8,7 +8,8 @@ cache: directories: - ../bundle before_install: - - gem update --system # https://github.com/travis-ci/travis-ci/issues/8978#issuecomment-354036443 + # We need to ensure the latest version of Rubygems, unless we're on an old Ruby. + - gem update --system $(ruby -e "puts '2.7.8' if ENV['RUBY_VERSION'] >= '2.3.0'") - gem install bundler - unset _JAVA_OPTIONS - "script/clone_all_rspec_repos" diff --git a/appveyor.yml b/appveyor.yml index b05934c5a..169c14c6f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. +# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 925ca90d3..3c249ecc9 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. +# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 8ef5c60df..6189bb24f 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. +# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 95828a55a..0d1278b2f 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. +# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index aa99cdade..b0987bc95 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. +# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 7c47745b9..3b3ab1e33 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-11-17T09:39:20+01:00 from the rspec-dev repo. +# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From a0cd04d47f10e3e60dcbe98b6e7c7491e9c1760d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 30 Dec 2018 16:30:41 +0000 Subject: [PATCH 1128/1179] Updated travis build scripts (from rspec-dev) (#1253) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 ++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index ef31eba07..41bcfc587 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 18e49d938..cf4a19fc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -25,6 +25,7 @@ rvm: - 2.3.8 - 2.4.5 - 2.5.3 + - 2.6.0 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index 169c14c6f..936b7e80e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 3c249ecc9..db91dc8af 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 6189bb24f..c54fa76bf 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 0d1278b2f..b4bea29bb 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index b0987bc95..16acda276 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 3b3ab1e33..7c867f337 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-21T13:15:32+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 84f525f3308beecf2eceab2a8a668a4f7aae2999 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 31 Dec 2018 15:34:09 +0000 Subject: [PATCH 1129/1179] Updated travis build scripts (from rspec-dev) (#1254) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 4 ++-- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 41bcfc587..38b3ff031 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index cf4a19fc7..19d151ef7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 936b7e80e..731de0e29 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index db91dc8af..4697238ab 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index c54fa76bf..0b7c31920 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -19,7 +19,7 @@ fi function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there - travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" + travis_retry eval "git clone https://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" fi; } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index b4bea29bb..9f86d88bf 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 16acda276..745633e62 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 7c867f337..af5d5c7f9 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-25T18:01:30+00:00 from the rspec-dev repo. +# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From f8c188e22a4e85858384a47649c21ab0c0a63553 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 4 Jan 2019 12:43:44 +0000 Subject: [PATCH 1130/1179] Updated travis build scripts (from rspec-dev) (#1255) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 6 ++---- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 10 +++++++++- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 15 +++++++++++++++ 9 files changed, 32 insertions(+), 11 deletions(-) create mode 100755 script/update_rubygems_and_install_bundler diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 38b3ff031..3130e2676 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 19d151ef7..79f9f1eed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -8,9 +8,7 @@ cache: directories: - ../bundle before_install: - # We need to ensure the latest version of Rubygems, unless we're on an old Ruby. - - gem update --system $(ruby -e "puts '2.7.8' if ENV['RUBY_VERSION'] >= '2.3.0'") - - gem install bundler + - "script/update_rubygems_and_install_bundler" - unset _JAVA_OPTIONS - "script/clone_all_rspec_repos" bundler_args: "--binstubs --standalone --without documentation --path ../bundle" diff --git a/appveyor.yml b/appveyor.yml index 731de0e29..369422723 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 4697238ab..dcd4d914d 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 0b7c31920..1642bdb81 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 9f86d88bf..cfdc471f7 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -57,6 +57,14 @@ function is_mri_2plus { fi } +function is_ruby_23_plus { + if ruby -e "exit(RUBY_VERSION.to_f >= 2.3)"; then + return 0 + else + return 1 + fi +} + function rspec_support_compatible { if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then return 0 diff --git a/script/run_build b/script/run_build index 745633e62..891a05e96 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index af5d5c7f9..8b89cc8bd 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2018-12-30T16:45:57+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler new file mode 100755 index 000000000..bcf7e86db --- /dev/null +++ b/script/update_rubygems_and_install_bundler @@ -0,0 +1,15 @@ +#!/bin/bash +# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +set -e +source script/functions.sh + +if is_ruby_23_plus; then + gem update --system + gem install bundler +else + echo "Warning installing older versions of Rubygems / Bundler" + gem update --system '2.7.8' + gem install bundler -v '1.17.3' +fi From e5cac67c257e753dc1dda8585c644d22fdf79b96 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 8 Jan 2019 13:18:20 +0000 Subject: [PATCH 1131/1179] Updated travis build scripts (from rspec-dev) (#1257) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 +-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 3130e2676..77255e27e 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 79f9f1eed..5a5bf12fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -40,7 +40,6 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx-3 - - rvm: ruby-2.6.0-preview1 fast_finish: true branches: only: diff --git a/appveyor.yml b/appveyor.yml index 369422723..08b759674 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index dcd4d914d..86054c821 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 1642bdb81..bfefca816 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index cfdc471f7..ca1f19273 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 891a05e96..701ca999f 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 8b89cc8bd..9ae189804 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index bcf7e86db..b59faab29 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. +# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From 38551c848403a8380fc5142c3c6c4b4bb1b1b00f Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 27 Jan 2019 13:59:35 +0000 Subject: [PATCH 1132/1179] Pin jruby-openssl on older JRuby (#1260) --- Gemfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index a8b61b4fc..8e6961a4a 100644 --- a/Gemfile +++ b/Gemfile @@ -40,7 +40,12 @@ if RUBY_VERSION < '2.0.0' || RUBY_ENGINE == 'java' end platforms :jruby do - gem "jruby-openssl" + if RUBY_VERSION < '1.9.0' + # Pin jruby-openssl on older Rubies + gem "jruby-openssl", "< 0.10.0" + else + gem "jruby-openssl" + end end eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') From fdc22806e2f3c6e61ffb09d4a69321037706cb28 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 2 Feb 2019 10:12:48 +0000 Subject: [PATCH 1133/1179] Pin child-process on older J Ruby (#1261) --- Gemfile | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 8e6961a4a..0975d219c 100644 --- a/Gemfile +++ b/Gemfile @@ -19,12 +19,19 @@ if RUBY_VERSION >= '2.4' && RUBY_ENGINE == 'ruby' gem 'rubocop', "~> 0.52.1" end -if RUBY_VERSION < '2.0.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) - gem 'ffi', '< 1.9.15' # allow ffi to be installed on older rubies on windows +# allow gems to be installed on older rubies and/or windows +if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) + gem 'ffi', '< 1.10' +elsif RUBY_VERSION < '2.0.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) + gem 'ffi', '< 1.9.15' elsif RUBY_VERSION < '1.9' gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19 else - gem 'ffi', '~> 1.9.25' + gem 'ffi', '> 1.9.24' # prevent Github security vulnerability warning +end + +if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) + gem "childprocess", "< 1.0.0" end ### deps for rdoc.info @@ -41,8 +48,10 @@ end platforms :jruby do if RUBY_VERSION < '1.9.0' - # Pin jruby-openssl on older Rubies + # Pin jruby-openssl on older J Ruby gem "jruby-openssl", "< 0.10.0" + # Pin child-process on older J Ruby + gem "childprocess", "< 1.0.0" else gem "jruby-openssl" end From efc79c5e7bf4816cb9c8541cbf9c7f6881657939 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Sun, 3 Feb 2019 23:28:31 +0100 Subject: [PATCH 1134/1179] Remove unneeded JRuby Gemfile filtering for ffi (#1263) --- Gemfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gemfile b/Gemfile index 0975d219c..1c5041bef 100644 --- a/Gemfile +++ b/Gemfile @@ -22,8 +22,6 @@ end # allow gems to be installed on older rubies and/or windows if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) gem 'ffi', '< 1.10' -elsif RUBY_VERSION < '2.0.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) - gem 'ffi', '< 1.9.15' elsif RUBY_VERSION < '1.9' gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19 else From 0d4f68e5ca8356159a3b2219f887badf0ac68e3f Mon Sep 17 00:00:00 2001 From: Alireza Bashiri Date: Thu, 14 Mar 2019 19:57:23 +0700 Subject: [PATCH 1135/1179] Replace act-arrange-assert with arrange-act-assert (#1268) Arrange => Setup Act => Exercise Assert => Verify --- features/basics/spies.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/basics/spies.feature b/features/basics/spies.feature index 7de9a6430..eba067bd0 100644 --- a/features/basics/spies.feature +++ b/features/basics/spies.feature @@ -1,7 +1,7 @@ Feature: Spies [Message expectations](./expecting-messages) put an example's expectation at the start, before you've invoked the - code-under-test. Many developers prefer using an act-arrange-assert (or given-when-then) + code-under-test. Many developers prefer using an arrange-act-assert (or given-when-then) pattern for structuring tests. Spies are an alternate type of test double that support this pattern by allowing you to expect that a message has been received after the fact, using `have_received`. From 4f6f4131fd24d1a89ba389216e213dd4c95f4646 Mon Sep 17 00:00:00 2001 From: Patrick Boatner Date: Fri, 15 Mar 2019 16:56:00 -0500 Subject: [PATCH 1136/1179] Update docs for raising errors with required params (#1259) * Update readme "and_raise" with error params I would like to update the documentation for raising errors with required parameters. I have run into an issue multiple times where I've tried to raise an error class which requires args for instantiation like ``` expect(double).to receive(:msg).and_raise(ErrorRequiringArgs) ``` without realising the issue. Ruby gives an error message about "wrong number of arguments", which has repeatedly led me (personally) down troubleshooting other issues. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb8dfe3fc..e61404129 100644 --- a/README.md +++ b/README.md @@ -327,7 +327,7 @@ expect(double).to receive(:msg).and_return(value) expect(double).to receive(:msg).exactly(3).times.and_return(value1, value2, value3) # returns value1 the first time, value2 the second, etc expect(double).to receive(:msg).and_raise(error) - # error can be an instantiated object or a class + # `error` can be an instantiated object (e.g. `StandardError.new(some_arg)`) or a class (e.g. `StandardError`) # if it is a class, it must be instantiable with no args expect(double).to receive(:msg).and_throw(:msg) expect(double).to receive(:msg).and_yield(values, to, yield) From a6e52c7b6dc12847ad8a362a6dc87c501c557048 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Mon, 1 Apr 2019 23:36:31 +0200 Subject: [PATCH 1137/1179] Updated travis build scripts (from rspec-dev) (#1270) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 9 ++++----- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 77255e27e..6a164bbcf 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 5a5bf12fd..9eea00e90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby -sudo: false email: false cache: directories: @@ -21,9 +20,9 @@ rvm: - 2.1 - 2.2.10 - 2.3.8 - - 2.4.5 - - 2.5.3 - - 2.6.0 + - 2.4.6 + - 2.5.5 + - 2.6.2 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index 08b759674..4c3a948b9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 86054c821..bdb1a02a1 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index bfefca816..c4e6fd362 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index ca1f19273..6edd3f06b 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 701ca999f..9c20410bc 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 9ae189804..503fbf3b7 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index b59faab29..70a0e2dd6 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. +# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From a7409d366c0edd6dc9e2f66d792938a1e9460397 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Wed, 3 Apr 2019 08:44:42 +0200 Subject: [PATCH 1138/1179] Add the ability to use "time" as "times" alias --- README.md | 3 +++ .../setting_constraints/receive_counts.feature | 18 ++++++++++++++++++ lib/rspec/mocks/any_instance/chain.rb | 1 + lib/rspec/mocks/matchers/have_received.rb | 2 +- lib/rspec/mocks/message_expectation.rb | 1 + spec/rspec/mocks/any_instance_spec.rb | 16 ++++++++++++++++ spec/rspec/mocks/at_most_spec.rb | 15 +++++++++++++++ spec/rspec/mocks/block_return_value_spec.rb | 2 +- ...mbining_implementation_instructions_spec.rb | 2 +- .../rspec/mocks/matchers/have_received_spec.rb | 6 +++--- 10 files changed, 60 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e61404129..e1ae6bbfd 100644 --- a/README.md +++ b/README.md @@ -285,12 +285,15 @@ expect(double).to receive(:msg).with(hash_excluding(:a => 5)) # first arg is a h ```ruby expect(double).to receive(:msg).once expect(double).to receive(:msg).twice +expect(double).to receive(:msg).exactly(n).time expect(double).to receive(:msg).exactly(n).times expect(double).to receive(:msg).at_least(:once) expect(double).to receive(:msg).at_least(:twice) +expect(double).to receive(:msg).at_least(n).time expect(double).to receive(:msg).at_least(n).times expect(double).to receive(:msg).at_most(:once) expect(double).to receive(:msg).at_most(:twice) +expect(double).to receive(:msg).at_most(n).time expect(double).to receive(:msg).at_most(n).times ``` diff --git a/features/setting_constraints/receive_counts.feature b/features/setting_constraints/receive_counts.feature index de9c7c828..0c7466269 100644 --- a/features/setting_constraints/receive_counts.feature +++ b/features/setting_constraints/receive_counts.feature @@ -5,12 +5,15 @@ Feature: Receive Counts * `expect(...).to receive(...).once` * `expect(...).to receive(...).twice` + * `expect(...).to receive(...).exactly(n).time` * `expect(...).to receive(...).exactly(n).times` * `expect(...).to receive(...).at_least(:once)` * `expect(...).to receive(...).at_least(:twice)` + * `expect(...).to receive(...).at_least(n).time` * `expect(...).to receive(...).at_least(n).times` * `expect(...).to receive(...).at_most(:once)` * `expect(...).to receive(...).at_most(:twice)` + * `expect(...).to receive(...).at_most(n).time` * `expect(...).to receive(...).at_most(n).times` If you don't specify an expected receive count, it defaults to `once`. @@ -49,6 +52,11 @@ Feature: Receive Counts account.open end + example "exactly(n).time" do + expect(logger).to receive(:account_opened).exactly(1).time + account.open + end + example "exactly(n).times" do expect(logger).to receive(:account_opened).exactly(3).times account.open @@ -69,6 +77,11 @@ Feature: Receive Counts account.open end + example "at_least(n).time" do + expect(logger).to receive(:account_opened).at_least(1).time + account.open + end + example "at_least(n).times" do expect(logger).to receive(:account_opened).at_least(3).times account.open @@ -86,6 +99,11 @@ Feature: Receive Counts account.open end + example "at_most(n).time" do + expect(logger).to receive(:account_opened).at_most(1).time + account.open + end + example "at_most(n).times" do expect(logger).to receive(:account_opened).at_most(3).times account.open diff --git a/lib/rspec/mocks/any_instance/chain.rb b/lib/rspec/mocks/any_instance/chain.rb index c9ce232af..74d864b3a 100644 --- a/lib/rspec/mocks/any_instance/chain.rb +++ b/lib/rspec/mocks/any_instance/chain.rb @@ -41,6 +41,7 @@ def self.record(method_name) record :thrice record :exactly record :times + record :time record :never record :at_least record :at_most diff --git a/lib/rspec/mocks/matchers/have_received.rb b/lib/rspec/mocks/matchers/have_received.rb index 5b9aa8f10..dce6974f9 100644 --- a/lib/rspec/mocks/matchers/have_received.rb +++ b/lib/rspec/mocks/matchers/have_received.rb @@ -5,7 +5,7 @@ module Matchers class HaveReceived include Matcher - COUNT_CONSTRAINTS = %w[exactly at_least at_most times once twice thrice] + COUNT_CONSTRAINTS = %w[exactly at_least at_most times time once twice thrice] ARGS_CONSTRAINTS = %w[with] CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w[ordered] diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 90e0a550c..1f805d02d 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -238,6 +238,7 @@ def times(&block) self.inner_implementation_action = block self end + alias time times # Expect a message not to be received at all. # diff --git a/spec/rspec/mocks/any_instance_spec.rb b/spec/rspec/mocks/any_instance_spec.rb index 4d5111d27..656f7d904 100644 --- a/spec/rspec/mocks/any_instance_spec.rb +++ b/spec/rspec/mocks/any_instance_spec.rb @@ -905,6 +905,22 @@ def foo; end end context "the 'exactly(n)' constraint" do + describe "time alias" do + it "passes for 1 invocation" do + expect_any_instance_of(klass).to receive(:foo).exactly(1).time + instance = klass.new + instance.foo + end + + it "fails for 2 invocations" do + expect_any_instance_of(klass).to receive(:foo).exactly(1).time + expect_fast_failure_from(klass.new) do |instance| + 2.times { instance.foo } + verify instance + end + end + end + it "passes for n invocations where n = 3" do expect_any_instance_of(klass).to receive(:foo).exactly(3).times instance = klass.new diff --git a/spec/rspec/mocks/at_most_spec.rb b/spec/rspec/mocks/at_most_spec.rb index 25a2a1ec5..e6220c78e 100644 --- a/spec/rspec/mocks/at_most_spec.rb +++ b/spec/rspec/mocks/at_most_spec.rb @@ -5,6 +5,12 @@ module Mocks @double = double end + it "passes when at_most(n) is called exactly 1 time" do + expect(@double).to receive(:do_something).at_most(1).time + @double.do_something + verify @double + end + it "passes when at_most(n) is called exactly n times" do expect(@double).to receive(:do_something).at_most(2).times @double.do_something @@ -82,6 +88,15 @@ module Mocks end end + it "fails fast when at_most(n) times method is called n plus 1 time" do + expect(@double).to receive(:do_something).at_most(1).time + @double.do_something + + expect_fast_failure_from(@double, /expected: at most 1 time.*received: 2 times/m) do + @double.do_something + end + end + it "fails fast when at_most(:once) and is called twice" do expect(@double).to receive(:do_something).at_most(:once) @double.do_something diff --git a/spec/rspec/mocks/block_return_value_spec.rb b/spec/rspec/mocks/block_return_value_spec.rb index 4f48a7970..f1ee7b131 100644 --- a/spec/rspec/mocks/block_return_value_spec.rb +++ b/spec/rspec/mocks/block_return_value_spec.rb @@ -70,7 +70,7 @@ describe "times" do it "returns the value of executing the block" do obj = Object.new - allow(obj).to receive(:foo).at_least(1).times { 'bar' } + allow(obj).to receive(:foo).at_least(1).time { 'bar' } expect(obj.foo('baz')).to eq('bar') end end diff --git a/spec/rspec/mocks/combining_implementation_instructions_spec.rb b/spec/rspec/mocks/combining_implementation_instructions_spec.rb index d5cf2072d..ce6ac1b92 100644 --- a/spec/rspec/mocks/combining_implementation_instructions_spec.rb +++ b/spec/rspec/mocks/combining_implementation_instructions_spec.rb @@ -54,7 +54,7 @@ def verify_combined_implementation it 'works when passing a block to `times`' do verify_combined_implementation do |dbl| - expect(dbl).to receive(:foo).exactly(1).times { @block_called = true } + expect(dbl).to receive(:foo).exactly(1).time { @block_called = true } end end diff --git a/spec/rspec/mocks/matchers/have_received_spec.rb b/spec/rspec/mocks/matchers/have_received_spec.rb index 01878d5e0..c67ab294e 100644 --- a/spec/rspec/mocks/matchers/have_received_spec.rb +++ b/spec/rspec/mocks/matchers/have_received_spec.rb @@ -286,7 +286,7 @@ module Mocks it 'fails when the message was received more times than expected' do _expect { - expect(the_dbl).to have_received(:expected_method).exactly(1).times + expect(the_dbl).to have_received(:expected_method).exactly(1).time }.to raise_error(/expected: 1 time.*received: 3 times/m) _expect { expect(the_dbl).to have_received(:expected_method).exactly(2).times @@ -463,7 +463,7 @@ module Mocks the_dbl.one the_dbl.two - expect(the_dbl).to have_received(:one).at_least(1).times.ordered + expect(the_dbl).to have_received(:one).at_least(1).time.ordered expect(the_dbl).to have_received(:two).once.ordered end @@ -495,7 +495,7 @@ module Mocks the_dbl.one _expect { - expect(the_dbl).to have_received(:one).at_least(1).times.ordered + expect(the_dbl).to have_received(:one).at_least(1).time.ordered expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) end From 07fb921829072b9a9adf0bc4d022f23bb205b311 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Mon, 8 Apr 2019 14:30:29 +0200 Subject: [PATCH 1139/1179] Change log for #1271 --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 94267b88a..6cc56bad2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,9 @@ Enhancements: * Improve thread safety of message expectations by using Mutex to prevent deadlocking errors. (Ry Biesemeyer, #1236) +* Add the ability to use `time` as `times` alias. For example: + `expect(Class).to receive(:method).exactly(1).time`. + (Pistos, Benoit Tigeot, #1271) ### 3.8.0 / 2018-08-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0) From f1b69945b0b9cfb33b3959985ce0b9ddd3b1844c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 8 Apr 2019 15:23:32 +0200 Subject: [PATCH 1140/1179] Update Changelog.md --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 6cc56bad2..69a410e00 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,7 +5,7 @@ Enhancements: * Improve thread safety of message expectations by using Mutex to prevent deadlocking errors. (Ry Biesemeyer, #1236) -* Add the ability to use `time` as `times` alias. For example: +* Add the ability to use `time` as an alias for `times`. For example: `expect(Class).to receive(:method).exactly(1).time`. (Pistos, Benoit Tigeot, #1271) From 55f0b116dc8c15a7acef9e7619bb0a3925bd18fa Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Thu, 18 Apr 2019 23:48:57 +0200 Subject: [PATCH 1141/1179] Updated travis build scripts (from rspec-dev) (#1273) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 4 ++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 6a164bbcf..2a69e8e97 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 9eea00e90..f2f95c8dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -22,7 +22,7 @@ rvm: - 2.3.8 - 2.4.6 - 2.5.5 - - 2.6.2 + - 2.6.3 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index 4c3a948b9..a808b1140 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index bdb1a02a1..7be22baaa 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index c4e6fd362..c80529881 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 6edd3f06b..d88cecc8f 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 9c20410bc..d027713c0 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 503fbf3b7..65602d879 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index 70a0e2dd6..3af7196d4 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-04-01T20:23:57+02:00 from the rspec-dev repo. +# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From 5f3f68f936f40b24c43e7389017bdc2c6743dd24 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Sun, 19 May 2019 12:11:51 +0200 Subject: [PATCH 1142/1179] Deal with new ffi Ruby version requirement (#1276) https://github.com/ffi/ffi/pull/683 Related: - https://github.com/ffi/ffi/issues/699 --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 1c5041bef..7da14ad0c 100644 --- a/Gemfile +++ b/Gemfile @@ -24,6 +24,8 @@ if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|min gem 'ffi', '< 1.10' elsif RUBY_VERSION < '1.9' gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19 +elsif RUBY_VERSION < '2.0' + gem 'ffi', '< 1.11.0' # ffi dropped Ruby 1.9 support in 1.11.0 else gem 'ffi', '> 1.9.24' # prevent Github security vulnerability warning end From b3f18682003fe9a8aaf3447151ab689aecdfe888 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 29 May 2019 05:56:05 -0500 Subject: [PATCH 1143/1179] Do not change method visibility when stubbing private class methods. (#1277) --- Changelog.md | 4 ++++ lib/rspec/mocks/method_double.rb | 2 +- spec/rspec/mocks/stub_spec.rb | 11 +++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 69a410e00..7ec111b6d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,10 @@ Enhancements: `expect(Class).to receive(:method).exactly(1).time`. (Pistos, Benoit Tigeot, #1271) +Bug Fixes: + +* Do not change the visibility of stubbed private class methods. (Kevin Boschert) + ### 3.8.0 / 2018-08-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 3f418bfbe..4ffdcdf26 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -59,7 +59,7 @@ def define_proxy_method return if @method_is_proxied save_original_implementation_callable! - definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility| + definition_target.class_exec(self, method_name, @original_visibility || visibility) do |method_double, method_name, visibility| define_method(method_name) do |*args, &block| method_double.proxy_method_invoked(self, *args, &block) end diff --git a/spec/rspec/mocks/stub_spec.rb b/spec/rspec/mocks/stub_spec.rb index 70b45b00e..efa82d37a 100644 --- a/spec/rspec/mocks/stub_spec.rb +++ b/spec/rspec/mocks/stub_spec.rb @@ -19,6 +19,7 @@ def existing_instance_method existing_private_instance_method end + private def existing_private_instance_method :original_value end @@ -84,6 +85,16 @@ def existing_private_instance_method expect { @stub.dup.foobar }.to raise_error NoMethodError, /foobar/ end + it "remains private when it stubs a private instance method" do + allow(@instance).to receive(:existing_private_instance_method).and_return(1) + expect { @instance.existing_private_instance_method }.to raise_error NoMethodError, /private method `existing_private_instance_method/ + end + + it "remains private when it stubs a private class method" do + allow(@class).to receive(:existing_private_class_method).and_return(1) + expect { @class.existing_private_class_method }.to raise_error NoMethodError, /private method `existing_private_class_method/ + end + context "using `with`" do it 'determines which value is returned' do allow(@stub).to receive(:foo).with(1) { :one } From d0cd8dfba88f6a802e94a4a8966e03196ce6385c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 29 May 2019 11:57:28 +0100 Subject: [PATCH 1144/1179] Tweak changelog for #1277 --- Changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 7ec111b6d..48de4443e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,7 +11,8 @@ Enhancements: Bug Fixes: -* Do not change the visibility of stubbed private class methods. (Kevin Boschert) +* Ensure stubbing methods does not change their visibility. + (Kevin Boschert, #1277) ### 3.8.0 / 2018-08-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0) From b789d70b76d5bde224796de6e5d2c5af2bbd291b Mon Sep 17 00:00:00 2001 From: Matijs van Zuijlen Date: Fri, 21 Jun 2019 18:14:02 +0200 Subject: [PATCH 1145/1179] Update Aruba to version 0.14.10 (#1279) - Bump dependency version - Fix deprecations - Limit version of transitive dependency on contracts gem on old Rubies --- Gemfile | 4 ++++ features/support/disallow_certain_apis.rb | 2 +- features/support/env.rb | 22 +++++++++++----------- rspec-mocks.gemspec | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Gemfile b/Gemfile index 7da14ad0c..98e72dd57 100644 --- a/Gemfile +++ b/Gemfile @@ -34,6 +34,10 @@ if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|min gem "childprocess", "< 1.0.0" end +if RUBY_VERSION < '1.9.2' + gem 'contracts', '~> 0.15.0' # is a dependency of aruba +end + ### deps for rdoc.info group :documentation do gem 'redcarpet', '2.1.1' unless RUBY_PLATFORM == 'java' diff --git a/features/support/disallow_certain_apis.rb b/features/support/disallow_certain_apis.rb index 08b93b4bf..85006995c 100644 --- a/features/support/disallow_certain_apis.rb +++ b/features/support/disallow_certain_apis.rb @@ -4,7 +4,7 @@ if defined?(Cucumber) require 'shellwords' Before('~@allow-old-syntax') do - set_env('SPEC_OPTS', "-r#{Shellwords.escape(__FILE__)}") + set_environment_variable('SPEC_OPTS', "-r#{Shellwords.escape(__FILE__)}") end else module DisallowOneLinerShould diff --git a/features/support/env.rb b/features/support/env.rb index 74a5e9517..c00d86c8b 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,22 +1,22 @@ require 'aruba/cucumber' require 'rspec/expectations' -Before do +Aruba.configure do |config| if RUBY_PLATFORM =~ /java/ || defined?(Rubinius) - @aruba_timeout_seconds = 60 + config.exit_timeout = 60 else - @aruba_timeout_seconds = 5 + config.exit_timeout = 5 end end -Aruba.configure do |config| - config.before_cmd do - set_env('JRUBY_OPTS', "-X-C #{ENV['JRUBY_OPTS']}") # disable JIT since these processes are so short lived +Before do + if RUBY_PLATFORM == 'java' + # disable JIT since these processes are so short lived + set_environment_variable('JRUBY_OPTS', "-X-C #{ENV['JRUBY_OPTS']}") end -end if RUBY_PLATFORM == 'java' -Aruba.configure do |config| - config.before_cmd do - set_env('RBXOPT', "-Xint=true #{ENV['RBXOPT']}") # disable JIT since these processes are so short lived + if defined?(Rubinius) + # disable JIT since these processes are so short lived + set_environment_variable('RBXOPT', "-Xint=true #{ENV['RBXOPT']}") end -end if defined?(Rubinius) +end diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index c752d7dc5..21e9a50e9 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -47,6 +47,6 @@ Gem::Specification.new do |s| s.add_development_dependency 'rake', '~> 10.0.0' s.add_development_dependency 'cucumber', '~> 1.3.15' - s.add_development_dependency 'aruba', '~> 0.6.2' # 0.7 is broken on ruby 1.8.7 + s.add_development_dependency 'aruba', '~> 0.14.10' s.add_development_dependency 'minitest', '~> 5.2' end From af72baa61793b4c2e440d3b23e1c3682f3e61ca7 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 8 Jul 2019 15:22:59 +0100 Subject: [PATCH 1146/1179] Updated travis build scripts (from rspec-dev) (#1282) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 6 ++++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 2a69e8e97..7c751869c 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index f2f95c8dd..37e6e4ec2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. +# In order to install old Rubies, we need to use old Ubuntu distibution. +dist: trusty language: ruby email: false cache: @@ -22,7 +24,7 @@ rvm: - 2.3.8 - 2.4.6 - 2.5.5 - - 2.6.3 + - 2.6.2 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index a808b1140..c53fb6904 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 7be22baaa..c7b78bbf8 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index c80529881..b20db0859 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index d88cecc8f..25f0ce1f3 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index d027713c0..aeb6df446 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 65602d879..18777f3e6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index 3af7196d4..2b9403a48 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-04-18T19:41:53+02:00 from the rspec-dev repo. +# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From 2aab10d3e7a9c1c500acd1e88a7a408670f8cc48 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Wed, 24 Jul 2019 16:28:43 +0200 Subject: [PATCH 1147/1179] Updated travis build scripts (from rspec-dev) (#1285) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 3 ++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 7c751869c..0a4e5d9e7 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 37e6e4ec2..e9f7b2bfa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # In order to install old Rubies, we need to use old Ubuntu distibution. diff --git a/appveyor.yml b/appveyor.yml index c53fb6904..bb21f476b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -39,3 +39,4 @@ environment: - ruby_version: 23-x64 - ruby_version: 24-x64 - ruby_version: 25-x64 + - ruby_version: 26-x64 diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index c7b78bbf8..cffd48cda 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index b20db0859..4f164fadf 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 25f0ce1f3..f3860dfd3 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index aeb6df446..fe6b7e060 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 18777f3e6..bb03e91c8 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index 2b9403a48..c07f1f02c 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-07-08T14:21:39+01:00 from the rspec-dev repo. +# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From a6a2d7d4e9f0b346cfe2e387f5ff74689e25b662 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 12 Sep 2019 17:00:34 +0100 Subject: [PATCH 1148/1179] Fix bug where array_including argument matcher could not be nested (#1291) --- lib/rspec/mocks/argument_matchers.rb | 2 ++ spec/rspec/mocks/array_including_matcher_spec.rb | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/argument_matchers.rb b/lib/rspec/mocks/argument_matchers.rb index f86dcd370..fe41744b6 100644 --- a/lib/rspec/mocks/argument_matchers.rb +++ b/lib/rspec/mocks/argument_matchers.rb @@ -241,6 +241,8 @@ def ===(actual) RSpec::Support::FuzzyMatcher.values_match?(expected_element, actual_element) end end + rescue NoMethodError + false end def description diff --git a/spec/rspec/mocks/array_including_matcher_spec.rb b/spec/rspec/mocks/array_including_matcher_spec.rb index 4f8c03afd..219250ff9 100644 --- a/spec/rspec/mocks/array_including_matcher_spec.rb +++ b/spec/rspec/mocks/array_including_matcher_spec.rb @@ -21,11 +21,11 @@ module ArgumentMatchers expect(array_including(1, 2, 3)).to be === [1, 2, 3] end - it "matches the same array, specified without square brackets" do + it "matches the same array, specified without square brackets" do expect(array_including(1, 2, 3)).to be === [1, 2, 3] end - it "matches the same array, which includes nested arrays" do + it "matches the same array, which includes nested arrays" do expect(array_including([1, 2], 3, 4)).to be === [[1, 2], 3, 4] end @@ -43,6 +43,13 @@ module ArgumentMatchers expect(dbl).to receive(:a_message).with(3, array_including(instance_of(klass))) dbl.a_message(3, [1, klass.new, 4]) end + + # regression check + it "is composable when nested" do + expect(array_including(1, array_including(2, 3), 4)).to be === [1, [2, 3], 4] + expect([[1, 2], 3, 4]).to match array_including(array_including(1, 2), 3, 4) + expect([1,[1,2]]).to match array_including(1, array_including(1,2)) + end end context "failing" do From a62ec6fc309680a21ad3a88409500181381b13f8 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 12 Sep 2019 17:02:28 +0100 Subject: [PATCH 1149/1179] Change log for #1291 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 48de4443e..3d28b1c7b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,8 @@ Bug Fixes: * Ensure stubbing methods does not change their visibility. (Kevin Boschert, #1277) +* Allow `array_including` argument matchers to be nested. + (Emmanuel Delmas, #1291) ### 3.8.0 / 2018-08-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0) From ac5fcb36e0db570e6c7d4122953b9f71dd66917c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 25 Sep 2019 16:55:58 +0100 Subject: [PATCH 1150/1179] Pin minitest on older Rubies (#1293) --- Gemfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Gemfile b/Gemfile index 98e72dd57..467512360 100644 --- a/Gemfile +++ b/Gemfile @@ -38,6 +38,11 @@ if RUBY_VERSION < '1.9.2' gem 'contracts', '~> 0.15.0' # is a dependency of aruba end +# Version 5.12 of minitest requires Ruby 2.4 +if RUBY_VERSION < '2.4.0' + gem 'minitest', '< 5.12.0' +end + ### deps for rdoc.info group :documentation do gem 'redcarpet', '2.1.1' unless RUBY_PLATFORM == 'java' From e037054d0d3f1a328bdef3077718eacbd2731c19 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 13 Jun 2019 09:49:17 +0200 Subject: [PATCH 1151/1179] Version 3.8.1 --- Changelog.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3d28b1c7b..23d11e3f1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.0...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.1...master) Enhancements: @@ -11,11 +11,17 @@ Enhancements: Bug Fixes: -* Ensure stubbing methods does not change their visibility. - (Kevin Boschert, #1277) * Allow `array_including` argument matchers to be nested. (Emmanuel Delmas, #1291) +### 3.8.1 / 2019-06-13 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.0...v3.8.1) + +Bug Fixes: + +* Ensure stubbing methods does not change their visibility. + (Kevin Boschert, #1277) + ### 3.8.0 / 2018-08-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0) From 10243d77ce5ab587c09c94bfa9ef9ec156d138e7 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 2 Oct 2019 15:58:03 +0100 Subject: [PATCH 1152/1179] Version 3.8.2 --- Changelog.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 23d11e3f1..86b182735 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.1...master) +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.2...master) Enhancements: @@ -9,7 +9,8 @@ Enhancements: `expect(Class).to receive(:method).exactly(1).time`. (Pistos, Benoit Tigeot, #1271) -Bug Fixes: +### 3.8.2 / 2019-10-02 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.1...v3.8.2) * Allow `array_including` argument matchers to be nested. (Emmanuel Delmas, #1291) From 903c342e14080c40a8aa62a604d13cf23963ecfa Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 8 Oct 2019 10:06:16 +0100 Subject: [PATCH 1153/1179] Version 3.9.0 --- Changelog.md | 4 ++-- lib/rspec/mocks/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 86b182735..182d25dd0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.2...master) +### 3.9.0 / 2019-10-07 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.2...v3.9.0) Enhancements: diff --git a/lib/rspec/mocks/version.rb b/lib/rspec/mocks/version.rb index a97d3cc5e..b04d335f0 100644 --- a/lib/rspec/mocks/version.rb +++ b/lib/rspec/mocks/version.rb @@ -3,7 +3,7 @@ module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.9.0.pre' + STRING = '3.10.0.pre' end end end From 5b897e8f74f3059aef43f1ed5f91719f2267a04e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 21 Oct 2019 10:25:38 +0100 Subject: [PATCH 1154/1179] Updated travis build scripts (from rspec-dev) (#1296) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 7 +++++-- script/predicate_functions.sh | 10 +++++++++- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 21 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 0a4e5d9e7..972219505 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index e9f7b2bfa..f353e5ccc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # In order to install old Rubies, we need to use old Ubuntu distibution. diff --git a/appveyor.yml b/appveyor.yml index bb21f476b..1f3f92c41 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index cffd48cda..988307c78 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 4f164fadf..58bdf780d 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -187,7 +187,10 @@ function run_all_spec_suites { fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" - fold "rspec-rails specs" run_spec_suite_for "rspec-rails" + + if rspec_rails_compatible; then + fold "rspec-rails specs" run_spec_suite_for "rspec-rails" + fi if rspec_support_compatible; then fold "rspec-support specs" run_spec_suite_for "rspec-support" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index f3860dfd3..f2f72b56a 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -65,6 +65,14 @@ function is_ruby_23_plus { fi } +function rspec_rails_compatible { + if is_ruby_23_plus; then + return 0 + else + return 1 + fi +} + function rspec_support_compatible { if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then return 0 diff --git a/script/run_build b/script/run_build index fe6b7e060..d39a91e71 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index bb03e91c8..a86ea2f58 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index c07f1f02c..0abbafdea 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-07-24T15:33:52+02:00 from the rspec-dev repo. +# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From e1077e39001d65b967262f5f555c9529ae29b154 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 5 Dec 2019 21:00:17 +0000 Subject: [PATCH 1155/1179] Updated common plaintext files (from rspec-dev) [ci skip] (#1300) --- .github/FUNDING.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..0e4dc862c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,7 @@ + + +github: [JonRowe] +open_collective: rspec From fa3b6a06b63d785a594723befc34edf9eed8c0f5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 5 Dec 2019 21:33:45 +0000 Subject: [PATCH 1156/1179] Updated common plaintext files (from rspec-dev) [ci skip] (#1301) --- .github/FUNDING.yml | 6 ++---- BUILD_DETAIL.md | 6 +++--- CODE_OF_CONDUCT.md | 10 +++++----- CONTRIBUTING.md | 6 +++--- DEVELOPMENT.md | 10 +++++----- REPORT_TEMPLATE.md | 2 +- 6 files changed, 19 insertions(+), 21 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 0e4dc862c..4fb398d24 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,5 @@ - +# This file was generated on 2019-12-05T21:32:23+00:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. github: [JonRowe] open_collective: rspec diff --git a/BUILD_DETAIL.md b/BUILD_DETAIL.md index 6a4fa7dd1..4fe227ed6 100644 --- a/BUILD_DETAIL.md +++ b/BUILD_DETAIL.md @@ -1,5 +1,5 @@ @@ -68,8 +68,8 @@ $ bin/cucumber ## YARD documentation -RSpec uses [YARD](http://yardoc.org/) for API documentation on the [rspec.info site](http://rspec.info/). -Our commitment to [SemVer](htp://semver.org) requires that we explicitly +RSpec uses [YARD](https://yardoc.org/) for API documentation on the [rspec.info site](https://rspec.info/). +Our commitment to [SemVer](https://semver.org) requires that we explicitly declare our public API, and our build uses YARD to ensure that every class, module and method has either been labeled `@private` or has at least some level of documentation. For new APIs, this forces us to make diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 9b73017c4..618d788d3 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,5 +1,5 @@ @@ -46,14 +46,14 @@ when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting one of the project maintainers listed at -http://rspec.info/about/. All complaints will be reviewed and investigated +https://rspec.info/about/. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at -[http://contributor-covenant.org/version/1/3/0/][version] +[https://contributor-covenant.org/version/1/3/0/][version] -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/3/0/ +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/3/0/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4dd7a1e13..fd85fed38 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ @@ -13,8 +13,8 @@ If you'd like to help make RSpec better, here are some ways you can contribute: - by running RSpec HEAD to help us catch bugs before new releases - by [reporting bugs you encounter](https://github.com/rspec/rspec-mocks/issues/new) with [report template](#report-template) - by [suggesting new features](https://github.com/rspec/rspec-mocks/issues/new) - - by improving RSpec's [Relish](https://relishapp.com/rspec) or [API](http://rspec.info/documentation/) documentation - - by improving [RSpec's website](http://rspec.info/) ([source](https://github.com/rspec/rspec.github.io)) + - by improving RSpec's [Relish](https://relishapp.com/rspec) or [API](https://rspec.info/documentation/) documentation + - by improving [RSpec's website](https://rspec.info/) ([source](https://github.com/rspec/rspec.github.io)) - by taking part in [feature and issue discussions](https://github.com/rspec/rspec-mocks/issues) - by adding a failing test for reproducible [reported bugs](https://github.com/rspec/rspec-mocks/issues) - by reviewing [pull requests](https://github.com/rspec/rspec-mocks/pulls) and suggesting improvements diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index d1b305a27..7e6e779d6 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,12 +1,12 @@ # Development Setup Generally speaking, you only need to clone the project and install -the dependencies with [Bundler](http://bundler.io/). You can either +the dependencies with [Bundler](https://bundler.io/). You can either get a full RSpec development environment using [rspec-dev](https://github.com/rspec/rspec-dev#README) or you can set this project up individually. @@ -22,7 +22,7 @@ Clone the repo: $ git clone git@github.com:rspec/rspec-mocks.git ``` -Install the dependencies using [Bundler](http://bundler.io/): +Install the dependencies using [Bundler](https://bundler.io/): ``` $ cd rspec-mocks @@ -31,7 +31,7 @@ $ bundle install To minimize boot time and to ensure we don't depend upon any extra dependencies loaded by Bundler, our CI builds avoid loading Bundler at runtime -by using Bundler's [`--standalone option`](http://myronmars.to/n/dev-blog/2012/03/faster-test-boot-times-with-bundler-standalone). +by using Bundler's [`--standalone option`](https://myronmars.to/n/dev-blog/2012/03/faster-test-boot-times-with-bundler-standalone). While not strictly necessary (many/most of our contributors do not do this!), if you want to exactly reproduce our CI builds you'll want to do the same: @@ -114,7 +114,7 @@ Here's a short, non-exhaustive checklist of things we typically ask contributors # Adding Docs -RSpec uses [YARD](http://yardoc.org/) for its API documentation. To +RSpec uses [YARD](https://yardoc.org/) for its API documentation. To ensure the docs render well, we recommend running a YARD server and viewing your edits in a browser. diff --git a/REPORT_TEMPLATE.md b/REPORT_TEMPLATE.md index eff8136ce..b569e8aaf 100644 --- a/REPORT_TEMPLATE.md +++ b/REPORT_TEMPLATE.md @@ -1,5 +1,5 @@ From b934fafd2b0699df8a18f6a590530c08a3a9508e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 18 Dec 2019 16:42:56 +0000 Subject: [PATCH 1157/1179] Updated travis build scripts (from rspec-dev) (#1303) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 5 ++--- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 6 +++--- 9 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 972219505..3f6d6a2fa 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index f353e5ccc..598151910 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # In order to install old Rubies, we need to use old Ubuntu distibution. diff --git a/appveyor.yml b/appveyor.yml index 1f3f92c41..68756416f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 988307c78..64f4ee8f5 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 58bdf780d..e8c6a5af4 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -6,7 +6,7 @@ source $SCRIPT_DIR/travis_functions.sh source $SCRIPT_DIR/predicate_functions.sh # If JRUBY_OPTS isn't set, use these. -# see http://docs.travis-ci.com/user/ci-environment/ +# see https://docs.travis-ci.com/user/ci-environment/ export JRUBY_OPTS=${JRUBY_OPTS:-"--server -Xcompile.invokedynamic=false"} SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` @@ -187,7 +187,6 @@ function run_all_spec_suites { fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" - if rspec_rails_compatible; then fold "rspec-rails specs" run_spec_suite_for "rspec-rails" fi diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index f2f72b56a..746a4985f 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index d39a91e71..e20840e3c 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index a86ea2f58..4a86428a6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index 0abbafdea..ba2ac2560 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,13 +1,13 @@ #!/bin/bash -# This file was generated on 2019-10-21T09:35:11+01:00 from the rspec-dev repo. +# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh if is_ruby_23_plus; then - gem update --system - gem install bundler + yes | gem update --system + yes | gem install bundler else echo "Warning installing older versions of Rubygems / Bundler" gem update --system '2.7.8' From 8efc835c14eb411cf5f8b160de7e0b992ce79c31 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 28 Dec 2019 10:32:54 +0000 Subject: [PATCH 1158/1179] Updates from rspec-dev (2019-12-26) (#1305) * Updated travis build scripts (from rspec-dev) * Skip documentation check on 2.7 --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 9 +++++---- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 20 ++++++++++++++++++-- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 30 insertions(+), 13 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 3f6d6a2fa..4e00c2c55 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 598151910..cd2c8230f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # In order to install old Rubies, we need to use old Ubuntu distibution. @@ -22,9 +22,10 @@ rvm: - 2.1 - 2.2.10 - 2.3.8 - - 2.4.6 - - 2.5.5 - - 2.6.2 + - 2.4.9 + - 2.5.7 + - 2.6.5 + - 2.7.0 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index 68756416f..fb9dfcf0d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 64f4ee8f5..72fa6df32 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index e8c6a5af4..16d5a2bad 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 746a4985f..b54fa61d6 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -57,6 +57,18 @@ function is_mri_2plus { fi } +function is_mri_27 { + if is_mri; then + if ruby -e "exit(RUBY_VERSION.to_f == 2.7)"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + function is_ruby_23_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 2.3)"; then return 0 @@ -89,7 +101,11 @@ function additional_specs_available { function documentation_enforced { if [ -x ./bin/yard ]; then if is_mri_2plus; then - return 0 + if is_mri_27; then + return 1 + else + return 0 + fi else return 1 fi diff --git a/script/run_build b/script/run_build index e20840e3c..f8ce7a4cd 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 4a86428a6..3770b23d8 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index ba2ac2560..1b4ff8b04 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. +# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From 407a8aa70cb10ad2c79cc2b47576d2b1f5aa7b5e Mon Sep 17 00:00:00 2001 From: Daniel Orner Date: Tue, 31 Dec 2019 10:04:08 -0500 Subject: [PATCH 1159/1179] =?UTF-8?q?#1308:=20Call=20when=5Fdeclaring=5Fve?= =?UTF-8?q?rifying=5Fdouble=20for=20any=5Finstance=20partial=20=E2=80=A6?= =?UTF-8?q?=20(#1309)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #1308: Call when_declaring_verifying_double for any_instance partial mocks * #1308: Code review fixes --- lib/rspec/mocks/any_instance/recorder.rb | 5 +++ .../verifying_doubles/construction_spec.rb | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/lib/rspec/mocks/any_instance/recorder.rb b/lib/rspec/mocks/any_instance/recorder.rb index 0db97af31..d30cc86cb 100644 --- a/lib/rspec/mocks/any_instance/recorder.rb +++ b/lib/rspec/mocks/any_instance/recorder.rb @@ -21,6 +21,11 @@ def initialize(klass) @backed_up_method_owner = {} @klass = klass @expectation_set = false + + return unless RSpec::Mocks.configuration.verify_partial_doubles? + RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| + block.call(ObjectReference.for(klass)) + end end # Initializes the recording a stub to be played back against any diff --git a/spec/rspec/mocks/verifying_doubles/construction_spec.rb b/spec/rspec/mocks/verifying_doubles/construction_spec.rb index a40df67a8..4a5c7dde4 100644 --- a/spec/rspec/mocks/verifying_doubles/construction_spec.rb +++ b/spec/rspec/mocks/verifying_doubles/construction_spec.rb @@ -60,6 +60,44 @@ module CustomModule instance_double(test_class, :some_method_defined_dynamically => true) end + + describe 'any_instance' do + let(:test_class) { Class.new(ClassThatDynamicallyDefinesMethods) } + let(:not_implemented_error) { "#{test_class} does not implement #some_invalid_method" } + + before(:each) do + RSpec.configuration.mock_with(:rspec) do |config| + config.verify_partial_doubles = true + config.when_declaring_verifying_double do |reference| + reference.target.define_attribute_methods! if reference.target == test_class + end + end + end + + it 'calls the callback for expect_any_instance_of' do + expect_any_instance_of(test_class).to receive(:some_method_defined_dynamically) + expect { + expect_any_instance_of(test_class).to receive(:some_invalid_method) + }.to raise_error(RSpec::Mocks::MockExpectationError, not_implemented_error) + expect(test_class.new.some_method_defined_dynamically).to eq(nil) + end + + it 'calls the callback for allow_any_instance_of' do + allow_any_instance_of(test_class).to receive(:some_method_defined_dynamically) + expect { + allow_any_instance_of(test_class).to receive(:some_invalid_method) + }.to raise_error(RSpec::Mocks::MockExpectationError, not_implemented_error) + expect(test_class.new.some_method_defined_dynamically).to eq(nil) + end + + it 'should not call the callback if verify_partial_doubles is off' do + RSpec.configuration.mock_with(:rspec) do |config| + config.verify_partial_doubles = false + end + + expect(test_class.method_defined?(:some_method_defined_dynamically)).to be_falsey + end + end end describe 'class doubles' do From aae3ff21ce567eed51d3e6b454175a541a76bd5e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 31 Dec 2019 15:07:52 +0000 Subject: [PATCH 1160/1179] Changelog for #1309 --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index 182d25dd0..91fcd48d2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.0...master) + +Bug Fixes: + +* Trigger `RSpec::Mocks.configuration.verifying_double_callbacks` when using + `allow_any_instance_of` or `expect_any_instance_of` (Daniel Orner, #1309) + ### 3.9.0 / 2019-10-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.2...v3.9.0) From e6057a61d8a15c4638dec116f68579d83fdd5ad5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 31 Dec 2019 15:11:23 +0000 Subject: [PATCH 1161/1179] Version 3.9.1 --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 91fcd48d2..a431905c5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### Development -[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.0...master) +### 3.9.1 / 2019-12-31 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.0...v3.9.1) Bug Fixes: From a15a2137f1890d262eba597bd17e1bb5bc72e0ce Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Thu, 23 Jan 2020 22:37:16 +0300 Subject: [PATCH 1162/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 28 ++++++++++++++-------- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 26 insertions(+), 18 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4e00c2c55..852893b2a 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index cd2c8230f..41253a3ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # In order to install old Rubies, we need to use old Ubuntu distibution. diff --git a/appveyor.yml b/appveyor.yml index fb9dfcf0d..d5eb832c7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 72fa6df32..bd137b3a7 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 16d5a2bad..505563f04 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index b54fa61d6..17516df41 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -57,6 +57,22 @@ function is_mri_2plus { fi } +function is_ruby_23_plus { + if ruby -e "exit(RUBY_VERSION.to_f >= 2.3)"; then + return 0 + else + return 1 + fi +} + +function is_ruby_25_plus { + if ruby -e "exit(RUBY_VERSION.to_f >= 2.5)"; then + return 0 + else + return 1 + fi +} + function is_mri_27 { if is_mri; then if ruby -e "exit(RUBY_VERSION.to_f == 2.7)"; then @@ -69,16 +85,8 @@ function is_mri_27 { fi } -function is_ruby_23_plus { - if ruby -e "exit(RUBY_VERSION.to_f >= 2.3)"; then - return 0 - else - return 1 - fi -} - function rspec_rails_compatible { - if is_ruby_23_plus; then + if is_ruby_25_plus; then return 0 else return 1 diff --git a/script/run_build b/script/run_build index f8ce7a4cd..f1b755274 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 3770b23d8..9574746a3 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index 1b4ff8b04..b569ac0aa 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2019-12-26T17:20:33+00:00 from the rspec-dev repo. +# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From 2957cda37298f972ac71ea63ffe21081b177fa2d Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Fri, 17 Jan 2020 14:24:34 +0100 Subject: [PATCH 1163/1179] Be able to set custom error message from rspec-expectation --- lib/rspec/mocks/message_expectation.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/mocks/message_expectation.rb b/lib/rspec/mocks/message_expectation.rb index 1f805d02d..cb5a39922 100644 --- a/lib/rspec/mocks/message_expectation.rb +++ b/lib/rspec/mocks/message_expectation.rb @@ -366,7 +366,7 @@ module ImplementationDetails attr_reader :message attr_reader :orig_object attr_writer :expected_received_count, :expected_from, :argument_list_matcher - protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation= + protected :expected_received_count=, :expected_from=, :error_generator=, :implementation= # @private attr_reader :type @@ -376,7 +376,7 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub type=:expectation, opts={}, &implementation_block) @type = type @error_generator = error_generator - @error_generator.opts = opts + @error_generator.opts = error_generator.opts.merge(opts) @expected_from = expected_from @method_double = method_double @orig_object = @method_double.object From 628156e76a7f7a41fd7588e3c08280d4ed2cf2df Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Fri, 24 Jan 2020 09:49:35 +0100 Subject: [PATCH 1164/1179] Add documentation for custom failure message --- features/basics/expecting_messages.feature | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/features/basics/expecting_messages.feature b/features/basics/expecting_messages.feature index 5d095ef84..51652f5b0 100644 --- a/features/basics/expecting_messages.feature +++ b/features/basics/expecting_messages.feature @@ -73,3 +73,40 @@ Feature: Expecting messages """ When I run `rspec negative_message_expectation_spec.rb` Then the examples should all pass + + Scenario: Failing positive message expectation with a custom failure message + Given a file named "example_spec.rb" with: + """ruby + RSpec.describe "An unfulfilled positive message expectation" do + it "triggers a failure" do + dbl = double + expect(dbl).to receive(:foo), "dbl never call :foo" + end + end + """ + When I run `rspec example_spec.rb --format documentation` + Then the output should contain: + """ + 1) An unfulfilled positive message expectation triggers a failure + Failure/Error: expect(dbl).to receive(:foo), "dbl never call :foo" + dbl never call :foo + """ + + Scenario: Failing negative message expectation with a custom failure message + Given a file named "example_spec.rb" with: + """ruby + RSpec.describe "A negative message expectation" do + it "fails when the message is received" do + dbl = double + expect(dbl).not_to receive(:foo), "dbl called :foo but is not supposed to" + dbl.foo + end + end + """ + When I run `rspec example_spec.rb --format documentation` + Then the output should contain: + """ + 1) A negative message expectation fails when the message is received + Failure/Error: dbl.foo + dbl called :foo but is not supposed to + """ From d8f0e832e91780d1db391da397601b826f6ea2f9 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 25 Jan 2020 08:55:12 +0000 Subject: [PATCH 1165/1179] Grammar tweak --- features/basics/expecting_messages.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/basics/expecting_messages.feature b/features/basics/expecting_messages.feature index 51652f5b0..cf36d23d9 100644 --- a/features/basics/expecting_messages.feature +++ b/features/basics/expecting_messages.feature @@ -80,7 +80,7 @@ Feature: Expecting messages RSpec.describe "An unfulfilled positive message expectation" do it "triggers a failure" do dbl = double - expect(dbl).to receive(:foo), "dbl never call :foo" + expect(dbl).to receive(:foo), "dbl never calls :foo" end end """ @@ -88,8 +88,8 @@ Feature: Expecting messages Then the output should contain: """ 1) An unfulfilled positive message expectation triggers a failure - Failure/Error: expect(dbl).to receive(:foo), "dbl never call :foo" - dbl never call :foo + Failure/Error: expect(dbl).to receive(:foo), "dbl never calls :foo" + dbl never calls :foo """ Scenario: Failing negative message expectation with a custom failure message From ae6e11d7f5100146c4b58b165975a29e2ccc60db Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Sun, 26 Jan 2020 12:38:31 +0100 Subject: [PATCH 1166/1179] Changelog for #1312 --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index a431905c5..4933612da 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### Development +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.1...master) + +Enhancements: +* Add the ability to set a custom error generator in `MessageExpectation`. + This will allow rspec-expectations to inject a custom failure message. + (Benoit Tigeot and Nicolas Zermati, #1312) + ### 3.9.1 / 2019-12-31 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.0...v3.9.1) From 81c17e89b2722cb225d602e08b600e4a4f6148a4 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Wed, 1 Jan 2020 15:39:23 +0100 Subject: [PATCH 1167/1179] Bump yard to support Ruby 2.7 See: https://github.com/lsegal/yard/releases/tag/v0.9.21 --- Gemfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 467512360..727fdfb11 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp end end -gem 'yard', '~> 0.9.12', :require => false +gem 'yard', '~> 0.9.24', :require => false # No need to run rubocop on earlier versions if RUBY_VERSION >= '2.4' && RUBY_ENGINE == 'ruby' @@ -45,8 +45,8 @@ end ### deps for rdoc.info group :documentation do - gem 'redcarpet', '2.1.1' unless RUBY_PLATFORM == 'java' - gem 'github-markup', '0.7.2' + gem 'redcarpet', :platform => :mri + gem 'github-markup', :platform => :mri end gem 'simplecov', '~> 0.8' From 98208af51925544af65c497cd2c4f331de1ad9ee Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Mon, 30 Mar 2020 16:31:25 +0200 Subject: [PATCH 1168/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 20 ++------------------ script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 10 insertions(+), 26 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 852893b2a..1d323b623 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 41253a3ef..c5b1a114b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # In order to install old Rubies, we need to use old Ubuntu distibution. diff --git a/appveyor.yml b/appveyor.yml index d5eb832c7..13e397a17 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index bd137b3a7..a6e915e81 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 505563f04..efc219177 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 17516df41..4e40628e4 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -73,18 +73,6 @@ function is_ruby_25_plus { fi } -function is_mri_27 { - if is_mri; then - if ruby -e "exit(RUBY_VERSION.to_f == 2.7)"; then - return 0 - else - return 1 - fi - else - return 1 - fi -} - function rspec_rails_compatible { if is_ruby_25_plus; then return 0 @@ -109,11 +97,7 @@ function additional_specs_available { function documentation_enforced { if [ -x ./bin/yard ]; then if is_mri_2plus; then - if is_mri_27; then - return 1 - else - return 0 - fi + return 0 else return 1 fi diff --git a/script/run_build b/script/run_build index f1b755274..851d576ce 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 9574746a3..7f0b6d965 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index b569ac0aa..c256d80c3 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2020-01-23T22:37:16+03:00 from the rspec-dev repo. +# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From 31f36b6d6e504862e72076451e4afd0239ed5135 Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Fri, 3 Apr 2020 18:53:23 +0300 Subject: [PATCH 1169/1179] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 10 +++++----- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- script/update_rubygems_and_install_bundler | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 1d323b623..29d06e357 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index c5b1a114b..ee1433ce8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # In order to install old Rubies, we need to use old Ubuntu distibution. @@ -22,10 +22,10 @@ rvm: - 2.1 - 2.2.10 - 2.3.8 - - 2.4.9 - - 2.5.7 - - 2.6.5 - - 2.7.0 + - 2.4.10 + - 2.5.8 + - 2.6.6 + - 2.7.1 - ruby-head - ree - rbx-3 diff --git a/appveyor.yml b/appveyor.yml index 13e397a17..06cc874aa 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index a6e915e81..97741443b 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index efc219177..06e8320c0 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 4e40628e4..6320130f2 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 851d576ce..493fc3bd1 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 7f0b6d965..3426b5562 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index c256d80c3..ae94b87a6 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2020-03-30T16:31:25+02:00 from the rspec-dev repo. +# This file was generated on 2020-04-03T18:53:23+03:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e From 19164994c26b1f09d8d0f853da7d3c2b8ebf43aa Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 7 Apr 2020 10:02:46 +0100 Subject: [PATCH 1170/1179] Update rake to avoid security warning --- Gemfile | 8 ++++++++ rspec-mocks.gemspec | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 727fdfb11..83f22f0d9 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,14 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp end end +if RUBY_VERSION < '1.9.3' + gem 'rake', '< 11.0.0' # rake 11 requires Ruby 1.9.3 or later +elsif RUBY_VERSION < '2.0.0' + gem 'rake', '< 12.0.0' # rake 12 requires Ruby 2.0.0 or later +else + gem 'rake', '> 12.3.2' +end + gem 'yard', '~> 0.9.24', :require => false # No need to run rubocop on earlier versions diff --git a/rspec-mocks.gemspec b/rspec-mocks.gemspec index 21e9a50e9..9fd5e9a95 100644 --- a/rspec-mocks.gemspec +++ b/rspec-mocks.gemspec @@ -45,7 +45,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency "diff-lcs", ">= 1.2.0", "< 2.0" - s.add_development_dependency 'rake', '~> 10.0.0' + s.add_development_dependency 'rake', '> 10.0.0' s.add_development_dependency 'cucumber', '~> 1.3.15' s.add_development_dependency 'aruba', '~> 0.14.10' s.add_development_dependency 'minitest', '~> 5.2' From 8623eb000fb8edd53bc842033663ff8644b191c3 Mon Sep 17 00:00:00 2001 From: expeehaa Date: Tue, 12 May 2020 00:56:11 +0200 Subject: [PATCH 1171/1179] Update RSpec::Mocks.with_temporary_scope to return the result of the passed block. --- lib/rspec/mocks.rb | 6 +++++- spec/rspec/mocks_spec.rb | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 95ea1de4c..5b23720c9 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -87,15 +87,19 @@ def self.expect_message(subject, message, opts={}, &block) # Call the passed block and verify mocks after it has executed. This allows # mock usage in arbitrary places, such as a `before(:all)` hook. + # Returns the result of the passed block. def self.with_temporary_scope setup + result = nil begin - yield + result = yield verify ensure teardown end + + result end class << self diff --git a/spec/rspec/mocks_spec.rb b/spec/rspec/mocks_spec.rb index 8257ba994..771431e63 100644 --- a/spec/rspec/mocks_spec.rb +++ b/spec/rspec/mocks_spec.rb @@ -209,6 +209,10 @@ }.to raise_error("boom") # rather than MockExpectationError end + it 'returns the result of the passed block' do + expect(RSpec::Mocks.with_temporary_scope { 5 }).to eq 5 + end + def capture_error yield rescue Exception => @error From f6cd53c01a9a5a818d8742df89ae029cb017ed48 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 12 May 2020 08:57:40 +0100 Subject: [PATCH 1172/1179] Tweak style --- lib/rspec/mocks.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 5b23720c9..a0f82456d 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -91,15 +91,13 @@ def self.expect_message(subject, message, opts={}, &block) def self.with_temporary_scope setup - result = nil begin result = yield verify + result ensure teardown end - - result end class << self From fc9af92570de090a7819f640e4fd266a17bb61cf Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 12 May 2020 09:04:56 +0100 Subject: [PATCH 1173/1179] Add yard docs --- lib/rspec/mocks.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index a0f82456d..c802f166d 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -87,7 +87,8 @@ def self.expect_message(subject, message, opts={}, &block) # Call the passed block and verify mocks after it has executed. This allows # mock usage in arbitrary places, such as a `before(:all)` hook. - # Returns the result of the passed block. + # + # @return [Object] the return value from the block def self.with_temporary_scope setup From 38351580c6090238d181b9957d5ef36c2bad0e8b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 12 May 2020 09:24:26 +0100 Subject: [PATCH 1174/1179] Remove trailing space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Oops 🙃 --- lib/rspec/mocks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index c802f166d..297779e5a 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -87,7 +87,7 @@ def self.expect_message(subject, message, opts={}, &block) # Call the passed block and verify mocks after it has executed. This allows # mock usage in arbitrary places, such as a `before(:all)` hook. - # + # # @return [Object] the return value from the block def self.with_temporary_scope setup From 1728885f65b25a9676c5ce54133ef8a1283e2e0d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 12 May 2020 10:47:36 +0100 Subject: [PATCH 1175/1179] Changelog for #1329 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4933612da..3c6d8641a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Enhancements: * Add the ability to set a custom error generator in `MessageExpectation`. This will allow rspec-expectations to inject a custom failure message. (Benoit Tigeot and Nicolas Zermati, #1312) +* Return the result of the block passed to `RSpec::Mocks.with_temporary_scope` + when block run. (@expeehaa, #1329) ### 3.9.1 / 2019-12-31 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.0...v3.9.1) From 5adf99fdecf36cf92fd55ef658c63f8221ee876d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 28 Jun 2020 11:44:11 +0100 Subject: [PATCH 1176/1179] Support both versions of `diff-lcs` in specs --- spec/rspec/mocks/diffing_spec.rb | 12 +++++++----- spec/rspec/mocks/formatting_spec.rb | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/spec/rspec/mocks/diffing_spec.rb b/spec/rspec/mocks/diffing_spec.rb index e2a1b8a49..3b1f91edf 100644 --- a/spec/rspec/mocks/diffing_spec.rb +++ b/spec/rspec/mocks/diffing_spec.rb @@ -2,6 +2,8 @@ require "pp" RSpec.describe "Diffs printed when arguments don't match" do + include RSpec::Support::Spec::DiffHelpers + before do allow(RSpec::Mocks.configuration).to receive(:color?).and_return(false) end @@ -68,7 +70,7 @@ expect(d).to receive(:foo).with(expected_hash) expect { d.foo(:bad => :hash) - }.to fail_with(/\A# received :foo with unexpected arguments\n expected: \(#{hash_regex_inspect expected_hash}\)\n got: \(#{hash_regex_inspect actual_hash}\)\nDiff:\n@@ \-1\,2 \+1\,2 @@\n\-\[#{hash_regex_inspect expected_hash}\]\n\+\[#{hash_regex_inspect actual_hash}\]\n\z/) + }.to fail_with(/\A# received :foo with unexpected arguments\n expected: \(#{hash_regex_inspect expected_hash}\)\n got: \(#{hash_regex_inspect actual_hash}\)\nDiff:\n@@ #{Regexp.escape one_line_header} @@\n\-\[#{hash_regex_inspect expected_hash}\]\n\+\[#{hash_regex_inspect actual_hash}\]\n\z/) end end @@ -101,7 +103,7 @@ def hash_regex_inspect(hash) expect(d).to receive(:foo).with([:a, :b, :c]) expect { d.foo([]) - }.to fail_with("# received :foo with unexpected arguments\n expected: ([:a, :b, :c])\n got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[[:a, :b, :c]]\n+[[]]\n") + }.to fail_with("# received :foo with unexpected arguments\n expected: ([:a, :b, :c])\n got: ([])\nDiff:\n@@ #{one_line_header} @@\n-[[:a, :b, :c]]\n+[[]]\n") end end @@ -117,7 +119,7 @@ def hash_regex_inspect(hash) d.foo([]) }.to fail_with("# received :foo with unexpected arguments\n" \ " expected: (#{collab_inspect})\n" \ - " got: ([])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_inspect}]\n+[[]]\n") + " got: ([])\nDiff:\n@@ #{one_line_header} @@\n-[#{collab_inspect}]\n+[[]]\n") end end end @@ -136,7 +138,7 @@ def hash_regex_inspect(hash) d.foo([:a, :b]) }.to fail_with("# received :foo with unexpected arguments\n" \ " expected: (#{collab_description})\n" \ - " got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[\"#{collab_description}\"]\n+[[:a, :b]]\n") + " got: ([:a, :b])\nDiff:\n@@ #{one_line_header} @@\n-[\"#{collab_description}\"]\n+[[:a, :b]]\n") end end end @@ -164,7 +166,7 @@ def inspect d.foo([:a, :b]) }.to fail_with("# received :foo with unexpected arguments\n" \ " expected: (#{collab_inspect})\n" \ - " got: ([:a, :b])\nDiff:\n@@ -1,2 +1,2 @@\n-[#{collab_pp}]\n+[[:a, :b]]\n") + " got: ([:a, :b])\nDiff:\n@@ #{one_line_header} @@\n-[#{collab_pp}]\n+[[:a, :b]]\n") end end end diff --git a/spec/rspec/mocks/formatting_spec.rb b/spec/rspec/mocks/formatting_spec.rb index a08b1776e..aed05bed3 100644 --- a/spec/rspec/mocks/formatting_spec.rb +++ b/spec/rspec/mocks/formatting_spec.rb @@ -3,6 +3,7 @@ RSpec.describe "Test doubles format well in failure messages" do include RSpec::Matchers::FailMatchers + include RSpec::Support::Spec::DiffHelpers RSpec::Matchers.define :format_in_failures_as do |expected| match do |dbl| @@ -101,7 +102,7 @@ def actual_formatting(double) }.to fail_with(<<-EOS.gsub(/^\s+\|/, '')) |expected [#] to include # |Diff: - |@@ -1,2 +1,2 @@ + |@@ #{one_line_header} @@ |-[#] |+[#] EOS From f6a84700c77dfb4591d5b0738d5c28e74775a73b Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 28 Jun 2020 11:45:59 +0100 Subject: [PATCH 1177/1179] Run CI on both versions of diff-lcs --- .travis.yml | 2 ++ Gemfile | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index ee1433ce8..2194559ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,8 @@ matrix: include: - rvm: jruby-1.7 env: JRUBY_OPTS='--dev --1.8' + - rvm: 2.7.1 + env: DIFF_LCS_VERSION='~> 1.3.0' allow_failures: - rvm: jruby-head - rvm: ruby-head diff --git a/Gemfile b/Gemfile index 83f22f0d9..14f719846 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,12 @@ else gem 'rake', '> 12.3.2' end +if ENV['DIFF_LCS_VERSION'] + gem 'diff-lcs', ENV['DIFF_LCS_VERSION'] +else + gem 'diff-lcs', '~> 1.4', '>= 1.4.3' +end + gem 'yard', '~> 0.9.24', :require => false # No need to run rubocop on earlier versions From 023dbbaa71cc223cf338c9d32fa9db2ebe227972 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 25 Jul 2020 16:56:03 +0100 Subject: [PATCH 1178/1179] Copy rspec/rspec#59 --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 3258f0cf6..bf3328d73 100644 --- a/Rakefile +++ b/Rakefile @@ -65,7 +65,7 @@ task :default => [:spec, :cucumber] task :verify_private_key_present do private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') unless File.exist?(private_key) - raise "Your private key is not present. This gem should not be built without that." + raise "Your private key is not present. This gem should not be built without it." end end From 0a52e0a86b126b4bab94d277b2ad99a7492dc37d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 2 Aug 2020 03:00:21 +0100 Subject: [PATCH 1179/1179] Add deprecation message --- README.md | 464 +-------------------------------------------- lib/rspec/mocks.rb | 2 + 2 files changed, 4 insertions(+), 462 deletions(-) diff --git a/README.md b/README.md index e1ae6bbfd..17fe604f8 100644 --- a/README.md +++ b/README.md @@ -1,463 +1,3 @@ -# RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.svg?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.svg)](https://codeclimate.com/github/rspec/rspec-mocks) -rspec-mocks is a test-double framework for rspec with support for method stubs, -fakes, and message expectations on generated test-doubles and real objects -alike. +# RSpec Mocks -## Install - - gem install rspec # for rspec-core, rspec-expectations, rspec-mocks - gem install rspec-mocks # for rspec-mocks only - -Want to run against the `master` branch? You'll need to include the dependent -RSpec repos as well. Add the following to your `Gemfile`: - -```ruby -%w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| - gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'master' -end -``` -## Contributing - -Once you've set up the environment, you'll need to cd into the working -directory of whichever repo you want to work in. From there you can run the -specs and cucumber features, and make patches. - -NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You -can treat each RSpec repo as an independent project. - -For information about contributing to RSpec, please refer to the following markdown files: -* [Build details](BUILD_DETAIL.md) -* [Code of Conduct](CODE_OF_CONDUCT.md) -* [Detailed contributing guide](CONTRIBUTING.md) -* [Development setup guide](DEVELOPMENT.md) - -## Test Doubles - -A test double is an object that stands in for another object in your system -during a code example. Use the `double` method, passing in an optional identifier, to create one: - -```ruby -book = double("book") -``` - -Most of the time you will want some confidence that your doubles resemble an -existing object in your system. Verifying doubles are provided for this -purpose. If the existing object is available, they will prevent you from adding -stubs and expectations for methods that do not exist or that have an invalid -number of parameters. - -```ruby -book = instance_double("Book", :pages => 250) -``` - -Verifying doubles have some clever tricks to enable you to both test in -isolation without your dependencies loaded while still being able to validate -them against real objects. More detail is available in [their -documentation](https://github.com/rspec/rspec-mocks/blob/master/features/verifying_doubles). - -Verifying doubles can also accept custom identifiers, just like double(), e.g.: - -```ruby -books = [] -books << instance_double("Book", :rspec_book, :pages => 250) -books << instance_double("Book", "(Untitled)", :pages => 5000) - -puts books.inspect # with names, it's clearer which were actually added -``` - -## Method Stubs - -A method stub is an implementation that returns a pre-determined value. Method -stubs can be declared on test doubles or real objects using the same syntax. -rspec-mocks supports 3 forms for declaring method stubs: - -```ruby -allow(book).to receive(:title) { "The RSpec Book" } -allow(book).to receive(:title).and_return("The RSpec Book") -allow(book).to receive_messages( - :title => "The RSpec Book", - :subtitle => "Behaviour-Driven Development with RSpec, Cucumber, and Friends") -``` - -You can also use this shortcut, which creates a test double and declares a -method stub in one statement: - -```ruby -book = double("book", :title => "The RSpec Book") -``` - -The first argument is a name, which is used for documentation and appears in -failure messages. If you don't care about the name, you can leave it out, -making the combined instantiation/stub declaration very terse: - -```ruby -double(:foo => 'bar') -``` - -This is particularly nice when providing a list of test doubles to a method -that iterates through them: - -```ruby -order.calculate_total_price(double(:price => 1.99), double(:price => 2.99)) -``` - -### Stubbing a chain of methods - -You can use `receive_message_chain` in place of `receive` to stub a chain of messages: - -```ruby -allow(double).to receive_message_chain("foo.bar") { :baz } -allow(double).to receive_message_chain(:foo, :bar => :baz) -allow(double).to receive_message_chain(:foo, :bar) { :baz } - -# Given any of the above forms: -double.foo.bar # => :baz -``` - -Chains can be arbitrarily long, which makes it quite painless to violate the Law of Demeter in violent ways, so you should consider any use of `receive_message_chain` a code smell. Even though not all code smells indicate real problems (think fluent interfaces), `receive_message_chain` still results in brittle examples. For example, if you write `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the implementation calls `foo.baz.bar`, the stub will not work. - -## Consecutive return values - -When a stub might be invoked more than once, you can provide additional -arguments to `and_return`. The invocations cycle through the list. The last -value is returned for any subsequent invocations: - -```ruby -allow(die).to receive(:roll).and_return(1, 2, 3) -die.roll # => 1 -die.roll # => 2 -die.roll # => 3 -die.roll # => 3 -die.roll # => 3 -``` - -To return an array in a single invocation, declare an array: - -```ruby -allow(team).to receive(:players).and_return([double(:name => "David")]) -``` - -## Message Expectations - -A message expectation is an expectation that the test double will receive a -message some time before the example ends. If the message is received, the -expectation is satisfied. If not, the example fails. - -```ruby -validator = double("validator") -expect(validator).to receive(:validate) { "02134" } -zipcode = Zipcode.new("02134", validator) -zipcode.valid? -``` - -## Test Spies - -Verifies the given object received the expected message during the course of -the test. For a message to be verified, the given object must be setup to spy -on it, either by having it explicitly stubbed or by being a null object double -(e.g. `double(...).as_null_object`). Convenience methods are provided to easily -create null object doubles for this purpose: - -```ruby -spy("invitation") # => same as `double("invitation").as_null_object` -instance_spy("Invitation") # => same as `instance_double("Invitation").as_null_object` -class_spy("Invitation") # => same as `class_double("Invitation").as_null_object` -object_spy("Invitation") # => same as `object_double("Invitation").as_null_object` -``` - -Verifying messages received in this way implements the Test Spy pattern. - -```ruby -invitation = spy('invitation') - -user.accept_invitation(invitation) - -expect(invitation).to have_received(:accept) - -# You can also use other common message expectations. For example: -expect(invitation).to have_received(:accept).with(mailer) -expect(invitation).to have_received(:accept).twice -expect(invitation).to_not have_received(:accept).with(mailer) - -# One can specify a return value on the spy the same way one would a double. -invitation = spy('invitation', :accept => true) -expect(invitation).to have_received(:accept).with(mailer) -expect(invitation.accept).to eq(true) -``` - -Note that `have_received(...).with(...)` is unable to work properly when -passed arguments are mutated after the spy records the received message. -For example, this does not work properly: - -```ruby -greeter = spy("greeter") - -message = "Hello" -greeter.greet_with(message) -message << ", World" - -expect(greeter).to have_received(:greet_with).with("Hello") -``` - -## Nomenclature - -### Mock Objects and Test Stubs - -The names Mock Object and Test Stub suggest specialized Test Doubles. i.e. -a Test Stub is a Test Double that only supports method stubs, and a Mock -Object is a Test Double that supports message expectations and method -stubs. - -There is a lot of overlapping nomenclature here, and there are many -variations of these patterns (fakes, spies, etc). Keep in mind that most of -the time we're talking about method-level concepts that are variations of -method stubs and message expectations, and we're applying to them to _one_ -generic kind of object: a Test Double. - -### Test-Specific Extension - -a.k.a. Partial Double, a Test-Specific Extension is an extension of a -real object in a system that is instrumented with test-double like -behaviour in the context of a test. This technique is very common in Ruby -because we often see class objects acting as global namespaces for methods. -For example, in Rails: - -```ruby -person = double("person") -allow(Person).to receive(:find) { person } -``` - -In this case we're instrumenting Person to return the person object we've -defined whenever it receives the `find` message. We can also set a message -expectation so that the example fails if `find` is not called: - -```ruby -person = double("person") -expect(Person).to receive(:find) { person } -``` - -RSpec replaces the method we're stubbing or mocking with its own -test-double-like method. At the end of the example, RSpec verifies any message -expectations, and then restores the original methods. - -## Expecting Arguments - -```ruby -expect(double).to receive(:msg).with(*args) -expect(double).to_not receive(:msg).with(*args) -``` - -You can set multiple expectations for the same message if you need to: - -```ruby -expect(double).to receive(:msg).with("A", 1, 3) -expect(double).to receive(:msg).with("B", 2, 4) -``` - -## Argument Matchers - -Arguments that are passed to `with` are compared with actual arguments -received using ===. In cases in which you want to specify things about the -arguments rather than the arguments themselves, you can use any of the -matchers that ship with rspec-expectations. They don't all make syntactic -sense (they were primarily designed for use with RSpec::Expectations), but -you are free to create your own custom RSpec::Matchers. - -rspec-mocks also adds some keyword Symbols that you can use to -specify certain kinds of arguments: - -```ruby -expect(double).to receive(:msg).with(no_args) -expect(double).to receive(:msg).with(any_args) -expect(double).to receive(:msg).with(1, any_args) # any args acts like an arg splat and can go anywhere -expect(double).to receive(:msg).with(1, kind_of(Numeric), "b") #2nd argument can be any kind of Numeric -expect(double).to receive(:msg).with(1, boolean(), "b") #2nd argument can be true or false -expect(double).to receive(:msg).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp -expect(double).to receive(:msg).with(1, anything(), "b") #2nd argument can be anything at all -expect(double).to receive(:msg).with(1, duck_type(:abs, :div), "b") #2nd argument can be object that responds to #abs and #div -expect(double).to receive(:msg).with(hash_including(:a => 5)) # first arg is a hash with a: 5 as one of the key-values -expect(double).to receive(:msg).with(array_including(5)) # first arg is an array with 5 as one of the key-values -expect(double).to receive(:msg).with(hash_excluding(:a => 5)) # first arg is a hash without a: 5 as one of the key-values -``` - -## Receive Counts - -```ruby -expect(double).to receive(:msg).once -expect(double).to receive(:msg).twice -expect(double).to receive(:msg).exactly(n).time -expect(double).to receive(:msg).exactly(n).times -expect(double).to receive(:msg).at_least(:once) -expect(double).to receive(:msg).at_least(:twice) -expect(double).to receive(:msg).at_least(n).time -expect(double).to receive(:msg).at_least(n).times -expect(double).to receive(:msg).at_most(:once) -expect(double).to receive(:msg).at_most(:twice) -expect(double).to receive(:msg).at_most(n).time -expect(double).to receive(:msg).at_most(n).times -``` - -## Ordering - -```ruby -expect(double).to receive(:msg).ordered -expect(double).to receive(:other_msg).ordered - # This will fail if the messages are received out of order -``` - -This can include the same message with different arguments: - -```ruby -expect(double).to receive(:msg).with("A", 1, 3).ordered -expect(double).to receive(:msg).with("B", 2, 4).ordered -``` - -## Setting Responses - -Whether you are setting a message expectation or a method stub, you can -tell the object precisely how to respond. The most generic way is to pass -a block to `receive`: - -```ruby -expect(double).to receive(:msg) { value } -``` - -When the double receives the `msg` message, it evaluates the block and returns -the result. - -```ruby -expect(double).to receive(:msg).and_return(value) -expect(double).to receive(:msg).exactly(3).times.and_return(value1, value2, value3) - # returns value1 the first time, value2 the second, etc -expect(double).to receive(:msg).and_raise(error) - # `error` can be an instantiated object (e.g. `StandardError.new(some_arg)`) or a class (e.g. `StandardError`) - # if it is a class, it must be instantiable with no args -expect(double).to receive(:msg).and_throw(:msg) -expect(double).to receive(:msg).and_yield(values, to, yield) -expect(double).to receive(:msg).and_yield(values, to, yield).and_yield(some, other, values, this, time) - # for methods that yield to a block multiple times -``` - -Any of these responses can be applied to a stub as well - -```ruby -allow(double).to receive(:msg).and_return(value) -allow(double).to receive(:msg).and_return(value1, value2, value3) -allow(double).to receive(:msg).and_raise(error) -allow(double).to receive(:msg).and_throw(:msg) -allow(double).to receive(:msg).and_yield(values, to, yield) -allow(double).to receive(:msg).and_yield(values, to, yield).and_yield(some, other, values, this, time) -``` - -## Arbitrary Handling - -Once in a while you'll find that the available expectations don't solve the -particular problem you are trying to solve. Imagine that you expect the message -to come with an Array argument that has a specific length, but you don't care -what is in it. You could do this: - -```ruby -expect(double).to receive(:msg) do |arg| - expect(arg.size).to eq 7 -end -``` - -If the method being stubbed itself takes a block, and you need to yield to it -in some special way, you can use this: - -```ruby -expect(double).to receive(:msg) do |&arg| - begin - arg.call - ensure - # cleanup - end -end -``` - -## Delegating to the Original Implementation - -When working with a partial mock object, you may occasionally -want to set a message expectation without interfering with how -the object responds to the message. You can use `and_call_original` -to achieve this: - -```ruby -expect(Person).to receive(:find).and_call_original -Person.find # => executes the original find method and returns the result -``` - -## Combining Expectation Details - -Combining the message name with specific arguments, receive counts and responses -you can get quite a bit of detail in your expectations: - -```ruby -expect(double).to receive(:<<).with("illegal value").once.and_raise(ArgumentError) -``` - -While this is a good thing when you really need it, you probably don't really -need it! Take care to specify only the things that matter to the behavior of -your code. - -## Stubbing and Hiding Constants - -See the [mutating constants -README](https://github.com/rspec/rspec-mocks/blob/master/features/mutating_constants/README.md) -for info on this feature. - -## Use `before(:example)`, not `before(:context)` - -Stubs in `before(:context)` are not supported. The reason is that all stubs and mocks get cleared out after each example, so any stub that is set in `before(:context)` would work in the first example that happens to run in that group, but not for any others. - -Instead of `before(:context)`, use `before(:example)`. - -## Settings mocks or stubs on any instance of a class - -rspec-mocks provides two methods, `allow_any_instance_of` and -`expect_any_instance_of`, that will allow you to stub or mock any instance -of a class. They are used in place of `allow` or `expect`: - -```ruby -allow_any_instance_of(Widget).to receive(:name).and_return("Wibble") -expect_any_instance_of(Widget).to receive(:name).and_return("Wobble") -``` - -These methods add the appropriate stub or expectation to all instances of -`Widget`. - -This feature is sometimes useful when working with legacy code, though in -general we discourage its use for a number of reasons: - -* The `rspec-mocks` API is designed for individual object instances, but this - feature operates on entire classes of objects. As a result there are some - semantically confusing edge cases. For example in - `expect_any_instance_of(Widget).to receive(:name).twice` it isn't clear - whether each specific instance is expected to receive `name` twice, or if two - receives total are expected. (It's the former.) -* Using this feature is often a design smell. It may be - that your test is trying to do too much or that the object under test is too - complex. -* It is the most complicated feature of `rspec-mocks`, and has historically - received the most bug reports. (None of the core team actively use it, - which doesn't help.) - - -## Further Reading - -There are many different viewpoints about the meaning of mocks and stubs. If -you are interested in learning more, here is some recommended reading: - -* Mock Objects: http://www.mockobjects.com/ -* Endo-Testing: http://www.ccs.neu.edu/research/demeter/related-work/extreme-programming/MockObjectsFinal.PDF -* Mock Roles, Not Objects: http://www.jmock.org/oopsla2004.pdf -* Test Double: http://www.martinfowler.com/bliki/TestDouble.html -* Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html -* Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html - -## Also see - -* [https://github.com/rspec/rspec](https://github.com/rspec/rspec) -* [https://github.com/rspec/rspec-core](https://github.com/rspec/rspec-core) -* [https://github.com/rspec/rspec-expectations](https://github.com/rspec/rspec-expectations) -* [https://github.com/rspec/rspec-rails](https://github.com/rspec/rspec-rails) +This branch is deprecated, please use main`. diff --git a/lib/rspec/mocks.rb b/lib/rspec/mocks.rb index 297779e5a..d145f3fcf 100644 --- a/lib/rspec/mocks.rb +++ b/lib/rspec/mocks.rb @@ -1,3 +1,5 @@ +warn "This branch is deprecated. Please use `main`." + require 'rspec/support' RSpec::Support.require_rspec_support 'caller_filter' RSpec::Support.require_rspec_support 'warnings'