Skip to content

Commit

Permalink
WIP for #336 (making any_instance optional and configurable)
Browse files Browse the repository at this point in the history
  • Loading branch information
alindeman committed Nov 3, 2013
1 parent c33bdbf commit 18a2ead
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 13 deletions.
19 changes: 19 additions & 0 deletions lib/rspec/mocks/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ def syntax=(values)
end
end

def enable_any_instance_mocks?
@enable_any_instance_mocks
end

def enable_any_instance_mocks=(arg)
@enable_any_instance_mocks = arg
if arg
Syntax.enable_any_instance
else
Syntax.disable_any_instance
end
end

def reset_any_instance_to_default
self.enable_any_instance_mocks = true
RSpec::Mocks::Syntax.warn_about_any_instance!
end

def syntax
syntaxes = []
syntaxes << :should if Syntax.should_enabled?
Expand Down Expand Up @@ -107,5 +125,6 @@ def self.configuration
end

configuration.reset_syntaxes_to_default
configuration.reset_any_instance_to_default
end
end
77 changes: 64 additions & 13 deletions lib/rspec/mocks/syntax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ def self.warn_about_should!
@warn_about_should = true
end

def self.warn_about_any_instance!
@warn_about_any_instance = true
end

# @api private
def self.warn_unless_should_configured(method_name)
if @warn_about_should
Expand All @@ -21,6 +25,17 @@ def self.warn_unless_should_configured(method_name)
end
end

def self.warn_unless_any_instance_configured(method_name)
if @warn_about_any_instance
RSpec.deprecate(
"Using `#{method_name}` explicitly without enabling any instance mocks/stubs",
:replacement => "`RSpec::Mocks::Configuration.enable_any_instance_mocks = true` to enable any instance mocks/stubs"
)

@warn_about_any_instance = 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)
Expand Down Expand Up @@ -75,16 +90,9 @@ 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
end
end

enable_should_any_instance(syntax_host)
end

# @api private
Expand Down Expand Up @@ -127,20 +135,59 @@ def receive_messages(message_return_value_hash)
def allow(target)
AllowanceTarget.new(target)
end
end

enable_expect_any_instance(syntax_host)

RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do
def expect(target)
ExpectationTarget.new(target)
end
end
end

def self.enable_any_instance(expect_syntax_host = ::RSpec::Mocks::ExampleMethods)
enable_expect_any_instance(expect_syntax_host)
enable_should_any_instance(default_should_syntax_host)
end

def self.enable_should_any_instance(syntax_host)
return unless should_enabled?(syntax_host)

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
end
end

def self.enable_expect_any_instance(syntax_host = ::RSpec::Mocks::ExampleMethods)
return unless expect_enabled?(syntax_host)

syntax_host.class_exec do
def expect_any_instance_of(klass)
AnyInstanceExpectationTarget.new(klass)
end

def allow_any_instance_of(klass)
::RSpec::Mocks::Syntax.warn_unless_any_instance_configured(__method__)
AnyInstanceAllowanceTarget.new(klass)
end
end
end

RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do
def expect(target)
ExpectationTarget.new(target)
end
def self.disable_any_instance(syntax_host = ::RSpec::Mocks::ExampleMethods)
return unless any_instance_enabled?(syntax_host)
syntax_host.class_exec do
undef allow_any_instance_of
undef expect_any_instance_of
end

Class.class_exec do
undef any_instance
end
end

Expand All @@ -162,6 +209,10 @@ def self.disable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods)
end
end

def self.any_instance_enabled?(syntax_host = ::RSpec::Mocks::ExampleMethods)
syntax_host.method_defined?(:allow_any_instance_of) || Class.method_defined?(:any_instance)
end

# @api private
# Indicates whether or not the should syntax is enabled.
def self.should_enabled?(syntax_host = default_should_syntax_host)
Expand Down
96 changes: 96 additions & 0 deletions spec/rspec/mocks/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,92 @@ def instance_methods_of(mod)
expect(instance_methods_of(mod_2)).to include(:stub, :should_receive)
end

shared_examples_for "configuring any instance mocks/stubs" do
def sandboxed
orig_any_instance = RSpec::Mocks.configuration.enable_any_instance_mocks?
yield
ensure
configure_any_instance(orig_any_instance)
end

around(:each) { |ex| sandboxed(&ex) }

shared_examples_for "any instance configuration" do
let(:expected_arguments) {
[
/Using.*without enabling/,
{:replacement=>"`RSpec::Mocks::Configuration.enable_any_instance_mocks = true` to enable any instance mocks/stubs"}
]
}

it "warns once when any instance has been implicitly enabled" do
configure_default_any_instance
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
invoke_any_instance_expectation
end

it "does not warn when any instance has been explicitly enabled" do
configure_any_instance(true)
expect(RSpec).not_to receive(:deprecate)
invoke_any_instance_expectation
end

it "disables any instance expectations when any_instance has been explicitly disabled" do
configure_any_instance(false)
expect { invoke_any_instance_expectation }.to raise_error(NoMethodError)
end

it "removes and readds the methods when the syntax is disabled then reenabled" do
configure_any_instance(false)
expect { invoke_any_instance_expectation }.to raise_error(NoMethodError)
configure_any_instance(true)
expect { invoke_any_instance_expectation }.not_to raise_error
end

it "removes and adds the methods when the syntax is disabled then the configuration is reset" do
configure_any_instance(false)
expect { invoke_any_instance_expectation }.to raise_error(NoMethodError)

configure_default_any_instance

expect(RSpec).to receive(:deprecate).with(*expected_arguments)
expect { invoke_any_instance_expectation }.not_to raise_error
end
end

context "allow_any_instance_of" do
def invoke_any_instance_expectation
allow_any_instance_of(Object).to receive(:foo)
end

it "includes a call site in the deprecation" do
configure_default_any_instance
expect_deprecation_with_call_site(__FILE__, __LINE__ + 1)
allow_any_instance_of(Object).to receive(:foo)
end

it_behaves_like "any instance configuration"
end

#context "expect_any_instance_of" do
# def invoke_any_instance_expectation
# @call_site = __LINE__+1
# allow_any_instance_of(Object).to receive(:foo)
# end

# it_behaves_like "any instance configuration"
#end

#context "any_instance" do
# def invoke_any_instance_expectation
# @call_site = __LINE__+1
# allow_any_instance_of(Object).to receive(:foo)
# end

# it_behaves_like "any instance configuration"
#end
end

shared_examples_for "configuring the syntax" do
def sandboxed
orig_syntax = RSpec::Mocks.configuration.syntax
Expand Down Expand Up @@ -194,6 +280,16 @@ def configure_default_syntax
RSpec::Mocks.configuration.reset_syntaxes_to_default
end
end

it_behaves_like "configuring any instance mocks/stubs" do
def configure_any_instance(enabled)
RSpec::Mocks.configuration.enable_any_instance_mocks = enabled
end

def configure_default_any_instance
RSpec::Mocks.configuration.reset_any_instance_to_default
end
end
end

describe "configuring using the rspec-core config API" do
Expand Down

0 comments on commit 18a2ead

Please sign in to comment.