Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: rspec/rspec-mocks
...
head fork: rspec/rspec-mocks
  • 13 commits
  • 14 files changed
  • 0 commit comments
  • 7 contributors
Commits on Jul 10, 2012
Uģis Ozols ugisozols Add missing 'be's to the README. 5288070
Justin Ko justinko Merge pull request #161 from ugisozols/master
Add missing 'be's to the README.
d9169ca
Commits on Jul 11, 2012
Myron Marston myronmarston Fix ruby warnings.
- lib/rspec/mocks/error_generator.rb:32: warning: assigned but unused variable - expected_args
- lib/rspec/mocks/error_generator.rb:33: warning: assigned but unused variable - actual_args
- lib/rspec/mocks/stub_const.rb:8: warning: shadowing outer local variable - name
- lib/rspec/mocks/stub_const.rb:12: warning: shadowing outer local variable - name
- spec/rspec/mocks/stub_const_spec.rb:60: warning: assigned but unused variable - orig_value
- lib/rspec/mocks/stub_const.rb:233: warning: instance variable @registered_with_mocks_space not initialized


Closes #162.
f36ad4d
Commits on Aug 01, 2012
David Chelimsky dchelimsky Don't modify dup on classes that don't support dup
Fixes #168.
b828604
Commits on Aug 03, 2012
daneget daneget Fix any_instance to handle methods defined on superclasses.
Previously, the recorder implementation created a SystemStackError.

Closes #152.
1272c8a
Myron Marston myronmarston Cleanup spec a bit. 306ee36
Myron Marston myronmarston Update changelog. f1a2682
Commits on Aug 05, 2012
David Chelimsky dchelimsky align the feature READMEs for stubs and message expectations e2826c6
David Chelimsky dchelimsky dev: update optional dev dependencies 37e095c
Commits on Aug 10, 2012
Jim Deville jredville fix formatting for relish df607ea
alindeman alindeman Merge pull request #173 from jredville/master
Clean up formatting for Relish
bb21bc1
Commits on Aug 11, 2012
Myron Marston myronmarston Fix use of const_defined? and const_get so it ignores top-level const…
…ants.

I didn't realize this previously, but these methods can pick up a top-level
constant when you don't intend it (e.g. ::Hash when checking MyGem.const_defined?("Hash")).
deec990
Commits on Aug 12, 2012
Myron Marston myronmarston 2.11.2 release. e7bd234
14 Changelog.md
View
@@ -1,3 +1,15 @@
+### 2.11.2 / 2012-08-11
+[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.1...v2.11.2)
+
+Bug fixes
+
+* Don't modify `dup` on classes that don't support `dup` (David Chelimsky)
+* Fix `any_instance` so that it works properly with methods defined on
+ a superclass. (Daniel Eguzkiza)
+* Fix `stub_const` so that it works properly for nested constants that
+ share a name with a top-level constant (e.g. "MyGem::Hash"). (Myron
+ Marston)
+
### 2.11.1 / 2012-07-09
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.0...v2.11.1)
@@ -13,7 +25,7 @@ Bug fixes
Enhancements
-* expose ArgumentListMatcher as a formal API
+* Expose ArgumentListMatcher as a formal API
* supports use by 3rd party mock frameworks like Surrogate
* Add `stub_const` API to stub constants for the duration of an
example (Myron Marston).
4 Gemfile-custom.sample
View
@@ -1,12 +1,12 @@
group :development do
gem 'interactive_rspec'
- gem 'relish', '~> 0.5.0'
+ gem 'relish', '~> 0.6.0'
gem 'guard-rspec', '0.5.0'
gem 'growl', '1.0.3'
gem 'spork', '0.9.0'
platform :mri do
- gem 'rb-fsevent', '~> 0.4.3.1'
+ gem 'rb-fsevent'
gem 'ruby-prof', '~> 0.10.0'
case RUBY_VERSION
4 README.md
View
@@ -152,8 +152,8 @@ 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 any kind of Numeric
-double.should_receive(:msg).with(1, boolean(), "b") #2nd argument can true or false
+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")
20 features/argument_matchers/README.md
View
@@ -4,20 +4,20 @@ Argument matchers can be used:
* In stubs to constrain the scope of the stubbed method
- obj.stub(:foo).with(:bar) do |arg|
- #do something for :bar
- end
- obj.stub(:foo).with(:baz) do |arg|
- #do something for :baz
- end
+ obj.stub(:foo).with(:bar) do |arg|
+ #do something for :bar
+ end
+ obj.stub(: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()
+ #create a double
+ obj = double()
- #expect a message with given args
- obj.should_receive(:message).with('an argument')
+ #expect a message with given args
+ obj.should_receive(:message).with('an argument')
If more control is needed, one can use a block
4 features/argument_matchers/type_matchers.feature
View
@@ -1,6 +1,8 @@
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()
+ 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:
37 features/message_expectations/README.md
View
@@ -7,7 +7,30 @@
obj.should_receive(:message)
# specify a return value
- obj.should_receive(:message) { 'this is the value to return' }
+ obj.should_receive(:message) { :value }
+ obj.should_receive(:message => :value)
+ obj.should_receive(: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
+
+ obj.should_receive(:message) do |arg1, arg2|
+ # set expectations about the args in this block
+ # and set a return value
+ end
+
+### Raising/Throwing
+
+ obj.should_receive(:message).and_raise("this error")
+ obj.should_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 }
### Argument constraints
@@ -40,19 +63,7 @@
obj.should_receive(:message).at_most(:twice)
obj.should_receive(:message).at_most(n).times
-### Raising/Throwing
-
- obj.should_receive(:message) { raise "this error" }
- obj.should_receive(:message) { throw :this_symbol }
-
### Ordering
obj.should_receive(:one).ordered
obj.should_receive(:two).ordered
-
-### Arbitrary handling
-
- obj.should_receive(:message) do |arg1, arg2|
- # set expectations about the args in this block
- # and set a return value
- end
12 features/method_stubs/README.md
View
@@ -1,12 +1,16 @@
### Stub return values
+ # create a double
+ obj = double()
+
+ # specify a return value
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.
+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
@@ -20,7 +24,7 @@ whereas the others are evaluated as they are read.
obj.stub(:message).and_raise("this error")
obj.stub(:message).and_throw(:this_symbol)
-You can also use the block format, for consistency with other stubs:
+You can also use the block format:
obj.stub(:message) { raise "this error" }
obj.stub(:message) { throw :this_symbol }
2  lib/rspec/mocks/any_instance.rb
View
@@ -54,7 +54,7 @@ def __recorder
private
def modify_dup_to_remove_mock_proxy_when_invoked
- unless self.method_defined?(:__rspec_original_dup)
+ if self.method_defined?(:dup) and !self.method_defined?(:__rspec_original_dup)
self.class_eval do
def __rspec_dup
__remove_mock_proxy
6 lib/rspec/mocks/any_instance/recorder.rb
View
@@ -175,7 +175,8 @@ def observe!(method_name)
backup_method!(method_name)
@klass.class_eval(<<-EOM, __FILE__, __LINE__)
def #{method_name}(*args, &blk)
- self.class.__recorder.playback!(self, :#{method_name})
+ klass = self.method(:#{method_name}).owner
+ klass.__recorder.playback!(self, :#{method_name})
self.__send__(:#{method_name}, *args, &blk)
end
EOM
@@ -186,7 +187,8 @@ def mark_invoked!(method_name)
@klass.class_eval(<<-EOM, __FILE__, __LINE__)
def #{method_name}(*args, &blk)
method_name = :#{method_name}
- invoked_instance = self.class.__recorder.instance_that_received(method_name)
+ klass = self.method(:#{method_name}).owner
+ invoked_instance = klass.__recorder.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
EOM
2  lib/rspec/mocks/error_generator.rb
View
@@ -31,7 +31,7 @@ def raise_unexpected_message_args_error(expectation, *args)
def raise_missing_default_stub_error(expectation,*args)
expected_args = format_args(*expectation.expected_args)
actual_args = args.collect {|a| format_args(*a)}.join(", ")
- __raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\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
61 lib/rspec/mocks/stub_const.rb
View
@@ -4,15 +4,54 @@ module Mocks
# constant stubbing.
# @api private
module RecursiveConstMethods
- def recursive_const_get(name)
- name.split('::').inject(Object) { |mod, name| mod.const_get name }
+ # 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; end`:
+ #
+ # On 1.8:
+ # - A.const_get("Hash") # => ::Hash
+ # - A.const_defined?("Hash") # => false
+ # - Neither method accepts the extra `inherit` argument
+ # On 1.9:
+ # - A.const_get("Hash") # => ::Hash
+ # - A.const_defined?("Hash") # => true
+ # - A.const_get("Hash", false) # => raises NameError
+ # - A.const_defined?("Hash", false) # => 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
+ 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
+ end
+
+ def recursive_const_get(const_name)
+ const_name.split('::').inject(Object) { |mod, name| get_const_defined_on(mod, name) }
end
- def recursive_const_defined?(name)
- name.split('::').inject([Object, '']) do |(mod, full_name), name|
+ def recursive_const_defined?(const_name)
+ const_name.split('::').inject([Object, '']) do |(mod, full_name), name|
yield(full_name, name) if block_given? && !mod.is_a?(Module)
- return false unless mod.const_defined?(name)
- [mod.const_get(name), [mod, name].join('::')]
+ return false unless const_defined_on?(mod, name)
+ [get_const_defined_on(mod, name), [mod, name].join('::')]
end
end
end
@@ -139,7 +178,7 @@ def to_constant
class DefinedConstantReplacer < BaseStubber
def stub
@context = recursive_const_get(@context_parts.join('::'))
- @original_value = @context.const_get(@const_name)
+ @original_value = get_const_defined_on(@context, @const_name)
constants_to_transfer = verify_constants_to_transfer!
@@ -160,7 +199,7 @@ def rspec_reset
def transfer_nested_constants(constants)
constants.each do |const|
- @stubbed_value.const_set(const, original_value.const_get(const))
+ @stubbed_value.const_set(const, get_const_defined_on(original_value, const))
end
end
@@ -202,9 +241,9 @@ class UndefinedConstantSetter < BaseStubber
def stub
remaining_parts = @context_parts.dup
@deepest_defined_const = @context_parts.inject(Object) do |klass, name|
- break klass unless klass.const_defined?(name)
+ break klass unless const_defined_on?(klass, name)
remaining_parts.shift
- klass.const_get(name)
+ get_const_defined_on(klass, name)
end
context = remaining_parts.inject(@deepest_defined_const) do |klass, name|
@@ -230,7 +269,7 @@ def rspec_reset
#
# @api private
def self.ensure_registered_with_mocks_space
- return if @registered_with_mocks_space
+ return if defined?(@registered_with_mocks_space) && @registered_with_mocks_space
::RSpec::Mocks.space.add(self)
@registered_with_mocks_space = true
end
2  lib/rspec/mocks/version.rb
View
@@ -1,7 +1,7 @@
module RSpec
module Mocks
module Version
- STRING = '2.11.1'
+ STRING = '2.11.2'
end
end
end
25 spec/rspec/mocks/any_instance_spec.rb
View
@@ -818,8 +818,33 @@ class RSpec::SampleRspecTestClass;end
o.some_method
lambda { o.dup.some_method }.should_not raise_error(SystemStackError)
end
+
+ it "doesn't bomb if the object doesn't support `dup`" do
+ klass = Class.new do
+ undef_method :dup
+ end
+ klass.any_instance
+ end
end
+ context "when directed at a method defined on a superclass" do
+ let(:sub_klass) { Class.new(klass) }
+
+ it "stubs the method correctly" do
+ klass.any_instance.stub(:existing_method).and_return("foo")
+ sub_klass.new.existing_method.should == "foo"
+ end
+
+ it "mocks the method correctly" do
+ instance_one = sub_klass.new
+ instance_two = sub_klass.new
+ expect do
+ klass.any_instance.should_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
+ end
end
end
end
5 spec/rspec/mocks/stub_const_spec.rb
View
@@ -57,7 +57,6 @@ def change_const_value_to(value)
end
it 'returns the stubbed value' do
- orig_value = const
stub_const(const_name, 7).should eq(7)
end
end
@@ -178,6 +177,10 @@ def change_const_value_to(value)
it_behaves_like "loaded constant stubbing", "TestClass::Nested"
end
+ context 'for an unloaded constant with nested name that matches a top-level constant' do
+ it_behaves_like "unloaded constant stubbing", "TestClass::Hash"
+ end
+
context 'for a loaded deeply nested constant' do
it_behaves_like "loaded constant stubbing", "TestClass::Nested::NestedEvenMore"
end

No commit comments for this range

Something went wrong with that request. Please try again.