rspec-mocks 2.14.0.rc1 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Changelog.md +25 -0
- data/features/message_expectations/allow_any_instance_of.feature +26 -0
- data/features/message_expectations/expect_any_instance_of.feature +27 -0
- data/features/message_expectations/expect_message_using_expect.feature +4 -0
- data/features/method_stubs/README.md +28 -2
- data/features/method_stubs/any_instance.feature +5 -1
- data/features/method_stubs/as_null_object.feature +6 -1
- data/features/method_stubs/simple_return_value_with_allow.feature +44 -0
- data/features/method_stubs/{simple_return_value.feature → simple_return_value_with_stub.feature} +0 -0
- data/features/method_stubs/stub_implementation.feature +23 -1
- data/lib/rspec/mocks.rb +35 -0
- data/lib/rspec/mocks/any_instance/chain.rb +27 -18
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +1 -28
- data/lib/rspec/mocks/any_instance/recorder.rb +1 -2
- data/lib/rspec/mocks/any_instance/stub_chain.rb +2 -1
- data/lib/rspec/mocks/error_generator.rb +7 -0
- data/lib/rspec/mocks/extensions/marshal.rb +7 -7
- data/lib/rspec/mocks/matchers/receive.rb +5 -1
- data/lib/rspec/mocks/message_expectation.rb +142 -63
- data/lib/rspec/mocks/method_double.rb +0 -9
- data/lib/rspec/mocks/proxy.rb +0 -5
- data/lib/rspec/mocks/proxy_for_nil.rb +2 -1
- data/lib/rspec/mocks/syntax.rb +15 -5
- data/lib/rspec/mocks/test_double.rb +2 -2
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/any_instance_spec.rb +26 -0
- data/spec/rspec/mocks/block_return_value_spec.rb +18 -0
- data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +205 -0
- data/spec/rspec/mocks/extensions/marshal_spec.rb +54 -0
- data/spec/rspec/mocks/matchers/receive_spec.rb +10 -0
- data/spec/rspec/mocks/mock_spec.rb +31 -3
- data/spec/rspec/mocks/partial_mock_spec.rb +3 -3
- data/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +81 -0
- data/spec/rspec/mocks/test_double_spec.rb +16 -6
- metadata +24 -9
data/Changelog.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
### 2.14.0 / 2013-07-06
|
2
|
+
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0.rc1...v2.14.0)
|
3
|
+
|
4
|
+
Enhancements:
|
5
|
+
|
6
|
+
* Document test spies in the readme. (Adarsh Pandit)
|
7
|
+
* Add an `array_including` matcher. (Sam Phippen)
|
8
|
+
* Add a syntax-agnostic API for mocking or stubbing a method. This is
|
9
|
+
intended for use by libraries such as rspec-rails that need to mock
|
10
|
+
or stub a method, and work regardless of the syntax the user has
|
11
|
+
configured (Paul Annesley, Myron Marston and Sam Phippen).
|
12
|
+
|
13
|
+
Bug Fixes:
|
14
|
+
|
15
|
+
* Fix `double` so that it sets up passed stubs correctly regardless of
|
16
|
+
the configured syntax (Paul Annesley).
|
17
|
+
* Allow a block implementation to be used in combination with
|
18
|
+
`and_yield`, `and_raise`, `and_return` or `and_throw`. This got fixed
|
19
|
+
in 2.13.1 but failed to get merged into master for the 2.14.0.rc1
|
20
|
+
release (Myron Marston).
|
21
|
+
* `Marshal.dump` does not unnecessarily duplicate objects when rspec-mocks has
|
22
|
+
not been fully initialized. This could cause errors when using `spork` or
|
23
|
+
similar preloading gems (Andy Lindeman).
|
24
|
+
|
1
25
|
### 2.14.0.rc1 / 2013-05-27
|
2
26
|
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1)
|
3
27
|
|
@@ -37,6 +61,7 @@ Bug fixes
|
|
37
61
|
(Luke Imhoff, Jon Rowe)
|
38
62
|
* Fix isolation of `allow_message_expectations_on_nil` (Jon Rowe)
|
39
63
|
* Use inspect to format actual arguments on expectations in failure messages (#280, Ben Langfeld)
|
64
|
+
* Protect against improperly initialised test doubles (#293) (Joseph Shraibman and Jon Rowe)
|
40
65
|
|
41
66
|
Deprecations
|
42
67
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: allow a message on any instance of a class
|
2
|
+
|
3
|
+
Use `allow_any_instance_of(Class).to receive` when you want to configure how
|
4
|
+
instances of the given class respond to a message without setting an
|
5
|
+
expectation that the message will be received.
|
6
|
+
|
7
|
+
Scenario: allowing a message on any instance of a class
|
8
|
+
Given a file named "example_spec.rb" with:
|
9
|
+
"""ruby
|
10
|
+
describe "any_instance.should_receive" do
|
11
|
+
before do
|
12
|
+
allow_any_instance_of(Object).to receive(:foo).and_return(:return_value)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "allows any instance of the class to receive the message" do
|
16
|
+
o = Object.new
|
17
|
+
expect(o.foo).to eq(:return_value)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "passes even if no instances receive that message" do
|
21
|
+
o = Object.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
"""
|
25
|
+
When I run `rspec example_spec.rb`
|
26
|
+
Then the examples should all pass
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: expect a message on any instance of a class
|
2
|
+
|
3
|
+
Use `expect_any_instance_of(Class).to receive` to set an expectation that one
|
4
|
+
(and only one) instance of a class receives a message before the example is
|
5
|
+
completed. The spec will fail if no instance receives a message.
|
6
|
+
|
7
|
+
Scenario: expect a message on any instance of a class
|
8
|
+
Given a file named "example_spec.rb" with:
|
9
|
+
"""ruby
|
10
|
+
describe "expect_any_instance_of" do
|
11
|
+
before do
|
12
|
+
expect_any_instance_of(Object).to receive(:foo).and_return(:return_value)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "verifies that one instance of the class receives the message" do
|
16
|
+
o = Object.new
|
17
|
+
expect(o.foo).to eq(:return_value)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "fails unless an instance receives that message" do
|
21
|
+
o = Object.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
"""
|
25
|
+
When I run `rspec example_spec.rb`
|
26
|
+
Then the output should contain "2 examples, 1 failure"
|
27
|
+
And the output should contain "1) expect_any_instance_of fails unless an instance receives that message"
|
@@ -4,6 +4,10 @@ Feature: expect message using `expect`
|
|
4
4
|
`object` should receive the message `:message` before the example is
|
5
5
|
completed.
|
6
6
|
|
7
|
+
Note: You can use `expect_any_instance_of` when you don't have a reference
|
8
|
+
to the object that receives a message in your test. For more information,
|
9
|
+
see the message_expectations/expect_any_instance_of feature.
|
10
|
+
|
7
11
|
Scenario: expect a message
|
8
12
|
Given a file named "spec/account_spec.rb" with:
|
9
13
|
"""ruby
|
@@ -3,7 +3,11 @@
|
|
3
3
|
# create a double
|
4
4
|
obj = double()
|
5
5
|
|
6
|
-
# specify a return value
|
6
|
+
# specify a return value using `:expect` syntax
|
7
|
+
allow(obj).to receive(:message) { :value }
|
8
|
+
allow(obj).to receive(:message).and_return(:value)
|
9
|
+
|
10
|
+
# specify a return value using `:should` syntax
|
7
11
|
obj.stub(:message) { :value }
|
8
12
|
obj.stub(:message => :value)
|
9
13
|
obj.stub(:message).and_return(:value)
|
@@ -14,18 +18,29 @@ block contents are evaluated lazily when the `obj` receives the
|
|
14
18
|
|
15
19
|
### Fake implementation
|
16
20
|
|
21
|
+
allow(obj).to receive(:message) do |arg1, arg2|
|
22
|
+
# set expectations about the args in this block
|
23
|
+
# and/or return value
|
24
|
+
end
|
25
|
+
|
17
26
|
obj.stub(:message) do |arg1, arg2|
|
18
27
|
# set expectations about the args in this block
|
19
|
-
# and/or
|
28
|
+
# and/or return a value
|
20
29
|
end
|
21
30
|
|
22
31
|
### Raising/Throwing
|
23
32
|
|
33
|
+
allow(obj).to receive(:message).and_raise("this error")
|
34
|
+
allow(obj).to receive(:message).and_throw(:this_symbol)
|
35
|
+
|
24
36
|
obj.stub(:message).and_raise("this error")
|
25
37
|
obj.stub(:message).and_throw(:this_symbol)
|
26
38
|
|
27
39
|
You can also use the block format:
|
28
40
|
|
41
|
+
allow(obj).to receive(:message) { raise "this error" }
|
42
|
+
allow(obj).to receive(:message) { throw :this_symbol }
|
43
|
+
|
29
44
|
obj.stub(:message) { raise "this error" }
|
30
45
|
obj.stub(:message) { throw :this_symbol }
|
31
46
|
|
@@ -33,15 +48,26 @@ You can also use the block format:
|
|
33
48
|
|
34
49
|
#### Explicit arguments
|
35
50
|
|
51
|
+
allow(obj).to receive(:message).with('an argument') { ... }
|
52
|
+
|
36
53
|
obj.stub(:message).with('an argument') { ... }
|
37
54
|
obj.stub(:message).with('more_than', 'one_argument') { ... }
|
38
55
|
|
39
56
|
#### Argument matchers
|
40
57
|
|
58
|
+
allow(obj).to receive(:message).with(anything()) { ... }
|
59
|
+
allow(obj).to receive(:message).with(an_instance_of(Money)) { ... }
|
60
|
+
allow(obj).to receive(:message).with(hash_including(:a => 'b')) { ... }
|
61
|
+
allow(obj).to receive(:message).with(array_including(1,2,3)) { ... }
|
62
|
+
# or
|
63
|
+
allow(obj).to receive(:message).with(array_including([1,2,3])) { ... }
|
64
|
+
|
41
65
|
obj.stub(:message).with(anything()) { ... }
|
42
66
|
obj.stub(:message).with(an_instance_of(Money)) { ... }
|
43
67
|
obj.stub(:message).with(hash_including(:a => 'b')) { ... }
|
44
68
|
|
45
69
|
#### Regular expressions
|
46
70
|
|
71
|
+
allow(obj).to receive(:message).with(/abc/) { ... }
|
72
|
+
|
47
73
|
obj.stub(:message).with(/abc/) { ... }
|
@@ -6,6 +6,10 @@ Feature: stub on any instance of a class
|
|
6
6
|
|
7
7
|
Messages can be stubbed on any class, including those in Ruby's core library.
|
8
8
|
|
9
|
+
Note: You can use `allow_any_instance_of` when you don't have a reference
|
10
|
+
to the object that receives a message in your test. For more information,
|
11
|
+
see the message_expectations/allow_any_instance_of feature.
|
12
|
+
|
9
13
|
Scenario: any_instance stub with a single return value
|
10
14
|
Given a file named "example_spec.rb" with:
|
11
15
|
"""ruby
|
@@ -129,4 +133,4 @@ Feature: stub on any instance of a class
|
|
129
133
|
end
|
130
134
|
"""
|
131
135
|
When I run `rspec stub_chain_spec.rb`
|
132
|
-
Then the examples should all pass
|
136
|
+
Then the examples should all pass
|
@@ -16,7 +16,12 @@ Feature: as_null_object
|
|
16
16
|
null_object.should respond_to(:an_undefined_method)
|
17
17
|
end
|
18
18
|
|
19
|
-
it "allows explicit stubs" do
|
19
|
+
it "allows explicit stubs using expect syntax" do
|
20
|
+
allow(null_object).to receive(:foo) { "bar" }
|
21
|
+
expect(null_object.foo).to eq("bar")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "allows explicit stubs using should syntax" do
|
20
25
|
null_object.stub(:foo) { "bar" }
|
21
26
|
null_object.foo.should eq("bar")
|
22
27
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Feature: allow with a simple return value
|
2
|
+
|
3
|
+
Use the `allow` method with the `receive` matcher on a test double or a real
|
4
|
+
object to tell the object to return a value (or values) in response to a given
|
5
|
+
message. Nothing happens if the message is never received.
|
6
|
+
|
7
|
+
Scenario: stub with no return value
|
8
|
+
Given a file named "example_spec.rb" with:
|
9
|
+
"""ruby
|
10
|
+
describe "a stub with no return value specified" do
|
11
|
+
let(:collaborator) { double("collaborator") }
|
12
|
+
|
13
|
+
it "returns nil" do
|
14
|
+
allow(collaborator).to receive(:message)
|
15
|
+
expect(collaborator.message).to be(nil)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
"""
|
19
|
+
When I run `rspec example_spec.rb`
|
20
|
+
Then the examples should all pass
|
21
|
+
|
22
|
+
Scenario: stubs with return values
|
23
|
+
Given a file named "example_spec.rb" with:
|
24
|
+
"""ruby
|
25
|
+
describe "a stub with a return value" do
|
26
|
+
context "specified in a block" do
|
27
|
+
it "returns the specified value" do
|
28
|
+
collaborator = double("collaborator")
|
29
|
+
allow(collaborator).to receive(:message) { :value }
|
30
|
+
expect(collaborator.message).to eq(:value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "specified with #and_return" do
|
35
|
+
it "returns the specified value" do
|
36
|
+
collaborator = double("collaborator")
|
37
|
+
allow(collaborator).to receive(:message).and_return(:value)
|
38
|
+
expect(collaborator.message).to eq(:value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
"""
|
43
|
+
When I run `rspec example_spec.rb`
|
44
|
+
Then the examples should all pass
|
data/features/method_stubs/{simple_return_value.feature → simple_return_value_with_stub.feature}
RENAMED
File without changes
|
@@ -3,7 +3,29 @@ Feature: stub with substitute implementation
|
|
3
3
|
You can stub an implementation of a method (a.k.a. fake) by passing a block
|
4
4
|
to the `stub` method.
|
5
5
|
|
6
|
-
Scenario: stub implementation
|
6
|
+
Scenario: stub implementation using `expect` syntax
|
7
|
+
Given a file named "stub_implementation_spec.rb" with:
|
8
|
+
"""ruby
|
9
|
+
describe "a stubbed implementation" do
|
10
|
+
it "works" do
|
11
|
+
object = Object.new
|
12
|
+
allow(object).to receive(:foo) do |arg|
|
13
|
+
if arg == :this
|
14
|
+
"got this"
|
15
|
+
elsif arg == :that
|
16
|
+
"got that"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(object.foo(:this)).to eq("got this")
|
21
|
+
expect(object.foo(:that)).to eq("got that")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
"""
|
25
|
+
When I run `rspec stub_implementation_spec.rb`
|
26
|
+
Then the output should contain "1 example, 0 failures"
|
27
|
+
|
28
|
+
Scenario: stub implementation using `should` syntax
|
7
29
|
Given a file named "stub_implementation_spec.rb" with:
|
8
30
|
"""ruby
|
9
31
|
describe "a stubbed implementation" do
|
data/lib/rspec/mocks.rb
CHANGED
@@ -30,6 +30,41 @@ module RSpec
|
|
30
30
|
space.any_instance_recorder_for(klass)
|
31
31
|
end
|
32
32
|
|
33
|
+
# Adds an allowance (stub) on `subject`
|
34
|
+
#
|
35
|
+
# @param subject the subject to which the message will be added
|
36
|
+
# @param message a symbol, representing the message that will be
|
37
|
+
# added.
|
38
|
+
# @param opts a hash of options, :expected_from is used to set the
|
39
|
+
# original call site
|
40
|
+
# @param block an optional implementation for the allowance
|
41
|
+
#
|
42
|
+
# @example Defines the implementation of `foo` on `bar`, using the passed block
|
43
|
+
# x = 0
|
44
|
+
# RSpec::Mocks.allow_message(bar, :foo) { x += 1 }
|
45
|
+
def allow_message(subject, message, opts={}, &block)
|
46
|
+
orig_caller = opts.fetch(:expected_from) { caller(1)[0] }
|
47
|
+
::RSpec::Mocks.proxy_for(subject).
|
48
|
+
add_stub(orig_caller, message.to_sym, opts, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets a message expectation on `subject`.
|
52
|
+
# @param subject the subject on which the message will be expected
|
53
|
+
# @param message a symbol, representing the message that will be
|
54
|
+
# expected.
|
55
|
+
# @param opts a hash of options, :expected_from is used to set the
|
56
|
+
# original call site
|
57
|
+
# @param block an optional implementation for the expectation
|
58
|
+
#
|
59
|
+
# @example Expect the message `foo` to receive `bar`, then call it
|
60
|
+
# RSpec::Mocks.expect_message(bar, :foo)
|
61
|
+
# bar.foo
|
62
|
+
def expect_message(subject, message, opts={}, &block)
|
63
|
+
orig_caller = opts.fetch(:expected_from) { caller(1)[0] }
|
64
|
+
::RSpec::Mocks.proxy_for(subject).
|
65
|
+
add_message_expectation(orig_caller, message.to_sym, opts, &block)
|
66
|
+
end
|
67
|
+
|
33
68
|
# @api private
|
34
69
|
KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method)
|
35
70
|
|
@@ -7,9 +7,7 @@ module RSpec
|
|
7
7
|
@expectation_block = block
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
private
|
12
|
-
|
10
|
+
module Customizations
|
13
11
|
# @macro [attach] record
|
14
12
|
# @method $1(*args, &block)
|
15
13
|
# Records the `$1` message for playback against an instance that
|
@@ -17,29 +15,31 @@ module RSpec
|
|
17
15
|
#
|
18
16
|
# @see RSpec::Mocks::MessageExpectation#$1
|
19
17
|
#
|
20
|
-
def record(method_name)
|
18
|
+
def self.record(method_name)
|
21
19
|
class_eval(<<-EOM, __FILE__, __LINE__ + 1)
|
22
20
|
def #{method_name}(*args, &block)
|
23
21
|
record(:#{method_name}, *args, &block)
|
24
22
|
end
|
25
23
|
EOM
|
26
24
|
end
|
25
|
+
|
26
|
+
record :and_return
|
27
|
+
record :and_raise
|
28
|
+
record :and_throw
|
29
|
+
record :and_yield
|
30
|
+
record :and_call_original
|
31
|
+
record :with
|
32
|
+
record :once
|
33
|
+
record :twice
|
34
|
+
record :any_number_of_times
|
35
|
+
record :exactly
|
36
|
+
record :times
|
37
|
+
record :never
|
38
|
+
record :at_least
|
39
|
+
record :at_most
|
27
40
|
end
|
28
41
|
|
29
|
-
|
30
|
-
record :and_raise
|
31
|
-
record :and_throw
|
32
|
-
record :and_yield
|
33
|
-
record :and_call_original
|
34
|
-
record :with
|
35
|
-
record :once
|
36
|
-
record :twice
|
37
|
-
record :any_number_of_times
|
38
|
-
record :exactly
|
39
|
-
record :times
|
40
|
-
record :never
|
41
|
-
record :at_least
|
42
|
-
record :at_most
|
42
|
+
include Customizations
|
43
43
|
|
44
44
|
# @private
|
45
45
|
def playback!(instance)
|
@@ -63,8 +63,17 @@ module RSpec
|
|
63
63
|
@expectation_fulfilled = true
|
64
64
|
end
|
65
65
|
|
66
|
+
def never
|
67
|
+
ErrorGenerator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated?
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
66
71
|
private
|
67
72
|
|
73
|
+
def negated?
|
74
|
+
messages.any? { |(message, *_), _| message.to_sym == :never }
|
75
|
+
end
|
76
|
+
|
68
77
|
def messages
|
69
78
|
@messages ||= []
|
70
79
|
end
|
@@ -36,34 +36,7 @@ module RSpec
|
|
36
36
|
}
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
40
|
-
# @api private
|
41
|
-
class NegativeExpectationChain < ExpectationChain
|
42
|
-
# `should_not_receive` causes a failure at the point in time the
|
43
|
-
# message is wrongly received, rather than during `rspec_verify`
|
44
|
-
# at the end of an example. Thus, we should always consider a
|
45
|
-
# negative expectation fulfilled for the purposes of end-of-example
|
46
|
-
# verification (which is where this is used).
|
47
|
-
def expectation_fulfilled?
|
48
|
-
true
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def create_message_expectation_on(instance)
|
54
|
-
proxy = ::RSpec::Mocks.proxy_for(instance)
|
55
|
-
expected_from = IGNORED_BACKTRACE_LINE
|
56
|
-
proxy.add_negative_message_expectation(expected_from, *@expectation_args, &@expectation_block)
|
57
|
-
end
|
58
|
-
|
59
|
-
def invocation_order
|
60
|
-
@invocation_order ||= {
|
61
|
-
:with => [nil],
|
62
|
-
:and_return => [:with, nil],
|
63
|
-
:and_raise => [:with, nil]
|
64
|
-
}
|
65
|
-
end
|
66
|
-
end
|
67
39
|
end
|
68
40
|
end
|
69
41
|
end
|
42
|
+
|
@@ -60,8 +60,7 @@ module RSpec
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def should_not_receive(method_name, &block)
|
63
|
-
|
64
|
-
message_chains.add(method_name, NegativeExpectationChain.new(method_name, &block))
|
63
|
+
should_receive(method_name, &block).never
|
65
64
|
end
|
66
65
|
|
67
66
|
# Removes any previously recorded stubs, stub_chains or message
|