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