Skip to content

Commit

Permalink
Merge pull request #1409 from rspec/verify-partial-doubles-by-default
Browse files Browse the repository at this point in the history
Verify partial doubles by default
  • Loading branch information
pirj committed Feb 2, 2021
2 parents eb5a621 + 43c1821 commit 91ddf83
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 33 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Breaking Changes:
* Extract monkey-patching `should_receive`/`stub` syntax. (Phil Pirozhkov, #1365)
* Remove the deprecated `RSpec::Mocks::CannotSupportArgMutationsError` constant.
(Phil Pirozhkov, #1400)
* Change the default setting for `RSpec::Mocks::Configuration#verify_partial_doubles`
to `true`. (Phil Pirozhkov, #1409)

Bug Fixes:

Expand Down
2 changes: 0 additions & 2 deletions features/basics/partial_test_doubles.feature
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ Feature: Partial test doubles
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
Expand Down
38 changes: 28 additions & 10 deletions features/verifying_doubles/partial_doubles.feature
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
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`](./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.
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 only turn off `verify_partial_doubles` (by setting it to false)
if you have a really good reason to.

Scenario: doubling an existing object
Given a file named "spec/user_spec.rb" with:
Expand All @@ -16,12 +15,6 @@ Feature: Partial doubles
"saved!" if user.save
end
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
RSpec.describe '#save_user' do
it 'renders message on success' do
user = User.new
Expand All @@ -32,3 +25,28 @@ Feature: Partial doubles
"""
When I run `rspec spec/user_spec.rb`
Then the output should contain "1 example, 1 failure"

Scenario: Stubbing a non-existent or a dynamic method
Given a file named "spec/user_spec.rb" with:
"""ruby
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = false
end
end
class Solution
def self.best
find_by_complexity
end
end
RSpec.describe '#find_by_complexity' do
it 'finds a simple solution' do
expect(Solution).to receive(:find_by_complexity).and_return("simple") # Method isn't defined
expect(Solution.best).to eq("simple")
end
end
"""
When I run `rspec spec/user_spec.rb`
Then the output should contain "1 example, 0 failures"
36 changes: 26 additions & 10 deletions features/working_with_legacy_code/any_instance.feature
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ Feature: Any Instance
"""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)
class Klass
def foo; end
end
allow_any_instance_of(Klass).to receive(:foo).and_return(:return_value)
o = Object.new
o = Klass.new
expect(o.foo).to eq(:return_value)
end
end
Expand All @@ -47,9 +50,13 @@ Feature: Any Instance
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')
class Klass
def foo; end
def bar; end
end
allow_any_instance_of(Klass).to receive_messages(:foo => 'foo', :bar => 'bar')
o = Object.new
o = Klass.new
expect(o.foo).to eq('foo')
expect(o.bar).to eq('bar')
end
Expand All @@ -65,10 +72,13 @@ Feature: Any Instance
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)
class Klass
def foo(one, two); end
end
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)
o = Object.new
o = Klass.new
expect(o.foo(:param_one, :param_two)).to eq(:result_one)
expect(o.foo(:param_three, :param_four)).to eq(:result_two)
end
Expand Down Expand Up @@ -99,15 +109,21 @@ Feature: Any Instance
"""ruby
RSpec.describe "expect_any_instance_of" do
before do
expect_any_instance_of(Object).to receive(:foo)
expect_any_instance_of(klass).to receive(:foo)
end
let(:klass) do
Class.new do
def foo; end
end
end
it "passes when an instance receives the message" do
Object.new.foo
klass.new.foo
end
it "fails when no instance receives the message" do
Object.new.to_s
klass.new.to_s
end
end
"""
Expand Down
20 changes: 13 additions & 7 deletions features/working_with_legacy_code/message_chains.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Feature: Message Chains
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:

Expand Down Expand Up @@ -74,19 +74,25 @@ Feature: Message Chains
Given a file named "receive_message_chain_spec.rb" with:
"""ruby
RSpec.describe "Using receive_message_chain on any instance of a class" do
let(:klass) do
Class.new do
def foo; end
end
end
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)
allow_any_instance_of(klass).to receive_message_chain("foo.bar") { :baz }
expect(klass.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)
allow_any_instance_of(klass).to receive_message_chain(:foo, :bar => :baz)
expect(klass.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)
allow_any_instance_of(klass).to receive_message_chain(:foo, :bar) { :baz }
expect(klass.new.foo.bar).to eq(:baz)
end
end
"""
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/mocks/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +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
@verify_partial_doubles = true
@temporarily_suppress_partial_double_verification = false
@color = false
end
Expand Down
7 changes: 7 additions & 0 deletions spec/rspec/mocks/any_instance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,13 @@ def foo; end
end

context "passing the receiver to the implementation block" do
let(:klass) do
Class.new do
def bees(arg)
end
end
end

context "when configured to pass the instance" do
include_context 'with isolated configuration'
before(:each) do
Expand Down
3 changes: 2 additions & 1 deletion spec/rspec/mocks/stub_implementation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ def obj.foo; :original; end
end

context "when partial doubles are not verified" do
before { expect(RSpec::Mocks.configuration.verify_partial_doubles?).to be false }
include_context "with isolated configuration"
before { RSpec::Mocks.configuration.verify_partial_doubles = false }
include_examples "stubbing `new` on class objects"
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ module Mocks
end

specify "trying to raise a class_double raises a TypeError" do
subject = Object.new
subject = Class.new do
def some_method; end
end.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')
Expand Down
5 changes: 4 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ def self.fake_matcher_description
require 'rspec/support/spec'

RSpec.configure do |config|
config.mock_with :rspec
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = false
end

config.color = true
config.order = :random

Expand Down

0 comments on commit 91ddf83

Please sign in to comment.