rspec-mocks 2.0.0.beta.4 → 2.0.0.beta.5
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/.autotest +3 -2
- data/VERSION +1 -1
- data/features/mocks/block_local_expectations.feature +0 -14
- data/features/mocks/mix_stubs_and_mocks.feature +2 -2
- data/features/mocks/warn_when_expectation_is_set_on_nil.feature +50 -0
- data/lib/rspec/mocks.rb +9 -9
- data/lib/rspec/mocks/argument_matchers.rb +1 -1
- data/lib/rspec/mocks/error_generator.rb +24 -8
- data/lib/rspec/mocks/framework.rb +1 -0
- data/lib/rspec/mocks/message_expectation.rb +1 -1
- data/lib/rspec/mocks/method_double.rb +156 -0
- data/lib/rspec/mocks/methods.rb +2 -2
- data/lib/rspec/mocks/mock.rb +17 -10
- data/lib/rspec/mocks/proxy.rb +71 -158
- data/lib/rspec/mocks/spec_methods.rb +19 -3
- data/rspec-mocks.gemspec +14 -10
- data/spec/rspec/mocks/any_number_of_times_spec.rb +1 -1
- data/spec/rspec/mocks/argument_expectation_spec.rb +2 -2
- data/spec/rspec/mocks/bug_report_10260_spec.rb +1 -1
- data/spec/rspec/mocks/bug_report_10263_spec.rb +8 -8
- data/spec/rspec/mocks/bug_report_15719_spec.rb +12 -12
- data/spec/rspec/mocks/bug_report_7611_spec.rb +1 -1
- data/spec/rspec/mocks/bug_report_7805_spec.rb +1 -1
- data/spec/rspec/mocks/bug_report_8165_spec.rb +1 -1
- data/spec/rspec/mocks/bug_report_957_spec.rb +1 -1
- data/spec/rspec/mocks/double_spec.rb +12 -0
- data/spec/rspec/mocks/failing_argument_matchers_spec.rb +7 -6
- data/spec/rspec/mocks/mock_ordering_spec.rb +54 -54
- data/spec/rspec/mocks/mock_space_spec.rb +4 -4
- data/spec/rspec/mocks/mock_spec.rb +55 -51
- data/spec/rspec/mocks/multiple_return_value_spec.rb +29 -7
- data/spec/rspec/mocks/nil_expectation_warning_spec.rb +8 -8
- data/spec/rspec/mocks/null_object_mock_spec.rb +2 -2
- data/spec/rspec/mocks/once_counts_spec.rb +1 -1
- data/spec/rspec/mocks/options_hash_spec.rb +1 -1
- data/spec/rspec/mocks/partial_mock_spec.rb +13 -7
- data/spec/rspec/mocks/passing_argument_matchers_spec.rb +1 -1
- data/spec/rspec/mocks/precise_counts_spec.rb +1 -1
- data/spec/rspec/mocks/record_messages_spec.rb +1 -1
- data/spec/rspec/mocks/stub_spec.rb +22 -22
- data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +12 -12
- data/spec/rspec/mocks/twice_counts_spec.rb +1 -1
- metadata +13 -9
data/.autotest
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.0.beta.
|
1
|
+
2.0.0.beta.5
|
@@ -1,9 +1,5 @@
|
|
1
1
|
Feature: block local expectations
|
2
2
|
|
3
|
-
In order to set message expectations on ...
|
4
|
-
As an RSpec user
|
5
|
-
I want to configure the evaluation context
|
6
|
-
|
7
3
|
Background:
|
8
4
|
Given a file named "account.rb" with:
|
9
5
|
"""
|
@@ -22,13 +18,8 @@ Feature: block local expectations
|
|
22
18
|
"""
|
23
19
|
require 'account'
|
24
20
|
|
25
|
-
Rspec.configure do |config|
|
26
|
-
config.mock_framework = :rspec
|
27
|
-
end
|
28
|
-
|
29
21
|
describe "account DSL" do
|
30
22
|
it "it succeeds when the block local receives the given call" do
|
31
|
-
account = Account.new
|
32
23
|
Account.should_receive(:create).and_yield do |account|
|
33
24
|
account.should_receive(:opening_balance).with(100, :USD)
|
34
25
|
end
|
@@ -47,13 +38,8 @@ Feature: block local expectations
|
|
47
38
|
"""
|
48
39
|
require 'account'
|
49
40
|
|
50
|
-
Rspec.configure do |config|
|
51
|
-
config.mock_framework = :rspec
|
52
|
-
end
|
53
|
-
|
54
41
|
describe "account DSL" do
|
55
42
|
it "fails when the block local does not receive the expected call" do
|
56
|
-
account = Account.new
|
57
43
|
Account.should_receive(:create).and_yield do |account|
|
58
44
|
account.should_receive(:opening_balance).with(100, :USD)
|
59
45
|
end
|
@@ -14,7 +14,7 @@ Feature: Spec and test together
|
|
14
14
|
|
15
15
|
describe "a stub in before" do
|
16
16
|
before(:each) do
|
17
|
-
@messenger =
|
17
|
+
@messenger = double('messenger').as_null_object
|
18
18
|
end
|
19
19
|
|
20
20
|
it "a" do
|
@@ -25,4 +25,4 @@ Feature: Spec and test together
|
|
25
25
|
end
|
26
26
|
"""
|
27
27
|
When I run "rspec stub_and_mocks_spec.rb -fs"
|
28
|
-
Then I should see "
|
28
|
+
Then I should see "received :foo with unexpected arguments"
|
@@ -0,0 +1,50 @@
|
|
1
|
+
Feature: warn when expectation is set on nil
|
2
|
+
|
3
|
+
Scenario: nil instance variable
|
4
|
+
Given a file named "example_spec.rb" with:
|
5
|
+
"""
|
6
|
+
Rspec.configure {|c| c.mock_with :rspec}
|
7
|
+
describe "something" do
|
8
|
+
it "does something" do
|
9
|
+
@i_do_not_exist.should_receive(:foo)
|
10
|
+
@i_do_not_exist.foo
|
11
|
+
end
|
12
|
+
end
|
13
|
+
"""
|
14
|
+
When I run "rspec example_spec.rb"
|
15
|
+
Then I should see "An expectation of :foo was set on nil"
|
16
|
+
|
17
|
+
Scenario: allow
|
18
|
+
Given a file named "example_spec.rb" with:
|
19
|
+
"""
|
20
|
+
Rspec.configure {|c| c.mock_with :rspec}
|
21
|
+
describe "something" do
|
22
|
+
it "does something" do
|
23
|
+
allow_message_expectations_on_nil
|
24
|
+
nil.should_receive(:foo)
|
25
|
+
nil.foo
|
26
|
+
end
|
27
|
+
end
|
28
|
+
"""
|
29
|
+
When I run "rspec example_spec.rb"
|
30
|
+
Then I should not see "An expectation"
|
31
|
+
|
32
|
+
Scenario: allow in one example, but not on another
|
33
|
+
Given a file named "example_spec.rb" with:
|
34
|
+
"""
|
35
|
+
Rspec.configure {|c| c.mock_with :rspec}
|
36
|
+
describe "something" do
|
37
|
+
it "does something (foo)" do
|
38
|
+
allow_message_expectations_on_nil
|
39
|
+
nil.should_receive(:foo)
|
40
|
+
nil.foo
|
41
|
+
end
|
42
|
+
it "does something (bar)" do
|
43
|
+
nil.should_receive(:bar)
|
44
|
+
nil.bar
|
45
|
+
end
|
46
|
+
end
|
47
|
+
"""
|
48
|
+
When I run "rspec example_spec.rb"
|
49
|
+
Then I should see "An expectation of :bar"
|
50
|
+
And I should not see "An expectation of :foo"
|
data/lib/rspec/mocks.rb
CHANGED
@@ -68,13 +68,13 @@ module Rspec
|
|
68
68
|
#
|
69
69
|
# You can create a mock in any specification (or setup) using:
|
70
70
|
#
|
71
|
-
#
|
71
|
+
# double(name, options={})
|
72
72
|
#
|
73
73
|
# The optional +options+ argument is a +Hash+. Currently the only supported
|
74
74
|
# option is +:null_object+. Setting this to true instructs the mock to ignore
|
75
75
|
# any messages it hasn’t been told to expect – and quietly return itself. For example:
|
76
76
|
#
|
77
|
-
#
|
77
|
+
# double("person", :null_object => true)
|
78
78
|
#
|
79
79
|
# == Creating a Stub
|
80
80
|
#
|
@@ -93,7 +93,7 @@ module Rspec
|
|
93
93
|
# mock expectations to existing classes and objects:
|
94
94
|
#
|
95
95
|
# Factory.should_receive(:find).with(id).and_return(value)
|
96
|
-
# obj.stub
|
96
|
+
# obj.stub(:to_i).and_return(3)
|
97
97
|
# etc ...
|
98
98
|
#
|
99
99
|
# == Expecting Messages
|
@@ -168,12 +168,12 @@ module Rspec
|
|
168
168
|
# not support any qualifiers about the message received (i.e. you can't specify arguments
|
169
169
|
# or receive counts):
|
170
170
|
#
|
171
|
-
# my_mock.stub
|
172
|
-
# my_mock.stub
|
173
|
-
# my_mock.stub
|
174
|
-
# my_mock.stub
|
175
|
-
# my_mock.stub
|
176
|
-
# my_mock.stub
|
171
|
+
# my_mock.stub(:sym).and_return(value)
|
172
|
+
# my_mock.stub(:sym).and_return(value1, value2, value3)
|
173
|
+
# my_mock.stub(:sym).and_raise(error)
|
174
|
+
# my_mock.stub(:sym).and_throw(:sym)
|
175
|
+
# my_mock.stub(:sym).and_yield(values,to,yield)
|
176
|
+
# my_mock.stub(:sym).and_yield(values,to,yield).and_yield(some,other,values,this,time)
|
177
177
|
#
|
178
178
|
# == Arbitrary Handling
|
179
179
|
#
|
@@ -3,7 +3,8 @@ module Rspec
|
|
3
3
|
class ErrorGenerator
|
4
4
|
attr_writer :opts
|
5
5
|
|
6
|
-
def initialize(target, name)
|
6
|
+
def initialize(target, name, options={})
|
7
|
+
@declared_as = options[:__declared_as] || 'Mock'
|
7
8
|
@target = target
|
8
9
|
@name = name
|
9
10
|
end
|
@@ -18,12 +19,18 @@ module Rspec
|
|
18
19
|
|
19
20
|
def raise_unexpected_message_args_error(expectation, *args)
|
20
21
|
expected_args = format_args(*expectation.expected_args)
|
21
|
-
actual_args =
|
22
|
-
__raise "#{intro}
|
22
|
+
actual_args = format_args(*args)
|
23
|
+
__raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def raise_similar_message_args_error(expectation, *args)
|
27
|
+
expected_args = format_args(*expectation.expected_args)
|
28
|
+
actual_args = args.collect {|a| format_args(*a)}.join(", ")
|
29
|
+
__raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
|
23
30
|
end
|
24
31
|
|
25
32
|
def raise_expectation_error(sym, expected_received_count, actual_received_count, *args)
|
26
|
-
__raise "#{intro}
|
33
|
+
__raise "(#{intro}).#{sym}#{format_args(*args)}\n expected: #{count_message(expected_received_count)}\n received: #{count_message(actual_received_count)}"
|
27
34
|
end
|
28
35
|
|
29
36
|
def raise_out_of_order_error(sym)
|
@@ -45,7 +52,17 @@ module Rspec
|
|
45
52
|
private
|
46
53
|
|
47
54
|
def intro
|
48
|
-
|
55
|
+
if @name
|
56
|
+
"#{@declared_as} #{@name.inspect}"
|
57
|
+
elsif Mock === @target
|
58
|
+
@declared_as
|
59
|
+
elsif Class === @target
|
60
|
+
"<#{@target.inspect} (class)>"
|
61
|
+
elsif @target
|
62
|
+
@target
|
63
|
+
else
|
64
|
+
"nil"
|
65
|
+
end
|
49
66
|
end
|
50
67
|
|
51
68
|
def __raise(message)
|
@@ -71,11 +88,10 @@ module Rspec
|
|
71
88
|
end
|
72
89
|
|
73
90
|
def pretty_print(count)
|
74
|
-
|
75
|
-
return "twice" if count == 2
|
76
|
-
return "#{count} times"
|
91
|
+
"#{count} time#{count == 1 ? '' : 's'}"
|
77
92
|
end
|
78
93
|
|
79
94
|
end
|
80
95
|
end
|
81
96
|
end
|
97
|
+
|
@@ -244,7 +244,7 @@ module Rspec
|
|
244
244
|
if similar_messages.empty?
|
245
245
|
@error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *@args_expectation.args)
|
246
246
|
else
|
247
|
-
@error_generator.
|
247
|
+
@error_generator.raise_similar_message_args_error(self, *@similar_messages)
|
248
248
|
end
|
249
249
|
end
|
250
250
|
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module Rspec
|
2
|
+
module Mocks
|
3
|
+
class MethodDouble < Hash
|
4
|
+
attr_reader :method_name
|
5
|
+
|
6
|
+
def initialize(object, method_name, proxy)
|
7
|
+
@method_name = method_name
|
8
|
+
@object = object
|
9
|
+
@proxy = proxy
|
10
|
+
@stashed = false
|
11
|
+
store(:expectations, [])
|
12
|
+
store(:stubs, [])
|
13
|
+
end
|
14
|
+
|
15
|
+
def expectations
|
16
|
+
self[:expectations]
|
17
|
+
end
|
18
|
+
|
19
|
+
def stubs
|
20
|
+
self[:stubs]
|
21
|
+
end
|
22
|
+
|
23
|
+
def visibility
|
24
|
+
if Mock === @object
|
25
|
+
'public'
|
26
|
+
elsif object_singleton_class.private_method_defined?(@method_name)
|
27
|
+
'private'
|
28
|
+
elsif object_singleton_class.protected_method_defined?(@method_name)
|
29
|
+
'protected'
|
30
|
+
else
|
31
|
+
'public'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def object_singleton_class
|
36
|
+
class << @object; self; end
|
37
|
+
end
|
38
|
+
|
39
|
+
def obfuscate(method_name)
|
40
|
+
"obfuscated_by_rspec_mocks__#{method_name}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def stashed_method_name
|
44
|
+
obfuscate(method_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def object_responds_to?(method_name)
|
48
|
+
if @proxy.already_proxied_respond_to?
|
49
|
+
@object.__send__(obfuscate(:respond_to?), method_name)
|
50
|
+
elsif method_name == :respond_to?
|
51
|
+
@proxy.already_proxied_respond_to
|
52
|
+
else
|
53
|
+
@object.respond_to?(method_name, true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def configure_method
|
58
|
+
$rspec_mocks.add(@object) if $rspec_mocks
|
59
|
+
warn_if_nil_class
|
60
|
+
stash_original_method
|
61
|
+
define_proxy_method
|
62
|
+
end
|
63
|
+
|
64
|
+
def stash_original_method
|
65
|
+
if object_responds_to?(@method_name)
|
66
|
+
stashed = stashed_method_name
|
67
|
+
orig = @method_name
|
68
|
+
object_singleton_class.class_eval do
|
69
|
+
alias_method(stashed, orig) if method_defined?(orig)
|
70
|
+
end
|
71
|
+
@stashed = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def define_proxy_method
|
76
|
+
method_name = @method_name
|
77
|
+
visibility_for_method = "#{visibility} :#{method_name}"
|
78
|
+
object_singleton_class.class_eval(<<-EOF, __FILE__, __LINE__)
|
79
|
+
def #{method_name}(*args, &block)
|
80
|
+
__mock_proxy.message_received :#{method_name}, *args, &block
|
81
|
+
end
|
82
|
+
#{visibility_for_method}
|
83
|
+
EOF
|
84
|
+
end
|
85
|
+
|
86
|
+
def restore_original_method
|
87
|
+
if @stashed
|
88
|
+
method_name = @method_name
|
89
|
+
stashed_method_name = self.stashed_method_name
|
90
|
+
object_singleton_class.instance_eval do
|
91
|
+
remove_method method_name
|
92
|
+
if method_defined?(stashed_method_name)
|
93
|
+
alias_method method_name, stashed_method_name
|
94
|
+
remove_method stashed_method_name
|
95
|
+
end
|
96
|
+
end
|
97
|
+
@stashed = false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def verify
|
102
|
+
expectations.each {|e| e.verify_messages_received}
|
103
|
+
end
|
104
|
+
|
105
|
+
def reset
|
106
|
+
reset_nil_expectations_warning
|
107
|
+
restore_original_method
|
108
|
+
clear
|
109
|
+
end
|
110
|
+
|
111
|
+
def clear
|
112
|
+
expectations.clear
|
113
|
+
stubs.clear
|
114
|
+
end
|
115
|
+
|
116
|
+
def add_expectation(error_generator, expectation_ordering, expected_from, opts, &block)
|
117
|
+
configure_method
|
118
|
+
expectation = if existing_stub = stubs.first
|
119
|
+
existing_stub.build_child(expected_from, block, 1, opts)
|
120
|
+
else
|
121
|
+
MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, block, 1, opts)
|
122
|
+
end
|
123
|
+
expectations << expectation
|
124
|
+
expectation
|
125
|
+
end
|
126
|
+
|
127
|
+
def add_negative_expectation(error_generator, expectation_ordering, expected_from, &implementation)
|
128
|
+
configure_method
|
129
|
+
expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, implementation)
|
130
|
+
expectations << expectation
|
131
|
+
expectation
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
|
135
|
+
configure_method
|
136
|
+
stub = MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, nil, :any, opts, &implementation)
|
137
|
+
stubs << stub
|
138
|
+
stub
|
139
|
+
end
|
140
|
+
|
141
|
+
def proxy_for_nil_class?
|
142
|
+
@object.nil?
|
143
|
+
end
|
144
|
+
|
145
|
+
def warn_if_nil_class
|
146
|
+
if proxy_for_nil_class? & Rspec::Mocks::Proxy.warn_about_expectations_on_nil
|
147
|
+
Kernel.warn("An expectation of :#{@method_name} was set on nil. Called from #{caller[4]}. Use allow_message_expectations_on_nil to disable warnings.")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def reset_nil_expectations_warning
|
152
|
+
Rspec::Mocks::Proxy.warn_about_expectations_on_nil = true if proxy_for_nil_class?
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
data/lib/rspec/mocks/methods.rb
CHANGED
@@ -9,7 +9,7 @@ module Rspec
|
|
9
9
|
__mock_proxy.add_negative_message_expectation(caller(1)[0], sym.to_sym, &block)
|
10
10
|
end
|
11
11
|
|
12
|
-
def stub
|
12
|
+
def stub(sym_or_hash, opts={}, &block)
|
13
13
|
if Hash === sym_or_hash
|
14
14
|
sym_or_hash.each {|method, value| stub!(method).and_return value }
|
15
15
|
else
|
@@ -17,7 +17,7 @@ module Rspec
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
alias_method :stub
|
20
|
+
alias_method :stub!, :stub
|
21
21
|
|
22
22
|
def stub_chain(*methods)
|
23
23
|
if methods.length > 1
|
data/lib/rspec/mocks/mock.rb
CHANGED
@@ -7,14 +7,14 @@ module Rspec
|
|
7
7
|
# only) == Options:
|
8
8
|
# * <tt>:null_object</tt> - if true, the mock object acts as a forgiving
|
9
9
|
# null object allowing any message to be sent to it.
|
10
|
-
def initialize(name=
|
10
|
+
def initialize(name=nil, stubs_and_options={})
|
11
11
|
if name.is_a?(Hash) && stubs_and_options.empty?
|
12
12
|
stubs_and_options = name
|
13
|
-
|
13
|
+
@name = nil
|
14
14
|
else
|
15
15
|
@name = name
|
16
16
|
end
|
17
|
-
@options =
|
17
|
+
@options = extract_options(stubs_and_options)
|
18
18
|
assign_stubs(stubs_and_options)
|
19
19
|
end
|
20
20
|
|
@@ -46,8 +46,19 @@ module Rspec
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
50
|
-
options
|
49
|
+
def extract_options(stubs_and_options)
|
50
|
+
options = {}
|
51
|
+
extract_option(stubs_and_options, options, :null_object)
|
52
|
+
extract_option(stubs_and_options, options, :__declared_as, 'Mock')
|
53
|
+
options
|
54
|
+
end
|
55
|
+
|
56
|
+
def extract_option(source, target, key, default=nil)
|
57
|
+
if source[key]
|
58
|
+
target[key] = source.delete(key)
|
59
|
+
elsif default
|
60
|
+
target[key] = default
|
61
|
+
end
|
51
62
|
end
|
52
63
|
|
53
64
|
def assign_stubs(stubs)
|
@@ -55,11 +66,7 @@ module Rspec
|
|
55
66
|
stub!(message).and_return(response)
|
56
67
|
end
|
57
68
|
end
|
58
|
-
|
59
|
-
def build_name_from_options(options)
|
60
|
-
vals = options.inject([]) {|coll, pair| coll << "#{pair.first}: #{pair.last.inspect}"}
|
61
|
-
@name = '{' + vals.join(', ') + '}'
|
62
|
-
end
|
63
69
|
end
|
64
70
|
end
|
65
71
|
end
|
72
|
+
|