hardmock 1.3.0 → 1.3.1
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/Rakefile +1 -1
- data/lib/hardmock.rb +39 -10
- data/lib/hardmock/stubbing.rb +62 -29
- data/test/functional/hardmock_test.rb +1 -3
- data/test/functional/stubbing_test.rb +85 -16
- metadata +2 -2
data/Rakefile
CHANGED
data/lib/hardmock.rb
CHANGED
@@ -15,6 +15,11 @@ module Hardmock
|
|
15
15
|
# with inherited, pre-mixed or post-added user teardowns.
|
16
16
|
def self.included(base) #:nodoc:#
|
17
17
|
base.class_eval do
|
18
|
+
# Core of our actual setup behavior
|
19
|
+
def hardmock_setup
|
20
|
+
prepare_hardmock_control
|
21
|
+
end
|
22
|
+
|
18
23
|
# Core of our actual teardown behavior
|
19
24
|
def hardmock_teardown
|
20
25
|
verify_mocks
|
@@ -24,6 +29,24 @@ module Hardmock
|
|
24
29
|
def self.method_added(symbol) #:nodoc:
|
25
30
|
end
|
26
31
|
|
32
|
+
if method_defined?(:setup) then
|
33
|
+
# Wrap existing setup
|
34
|
+
alias_method :old_setup, :setup
|
35
|
+
define_method(:new_setup) do
|
36
|
+
begin
|
37
|
+
hardmock_setup
|
38
|
+
ensure
|
39
|
+
old_setup
|
40
|
+
end
|
41
|
+
end
|
42
|
+
else
|
43
|
+
# We don't need to account for previous setup
|
44
|
+
define_method(:new_setup) do
|
45
|
+
hardmock_setup
|
46
|
+
end
|
47
|
+
end
|
48
|
+
alias_method :setup, :new_setup
|
49
|
+
|
27
50
|
if method_defined?(:teardown) then
|
28
51
|
# Wrap existing teardown
|
29
52
|
alias_method :old_teardown, :teardown
|
@@ -55,6 +78,17 @@ module Hardmock
|
|
55
78
|
end
|
56
79
|
end
|
57
80
|
end
|
81
|
+
when :setup
|
82
|
+
unless method_defined?(:user_setup)
|
83
|
+
alias_method :user_setup, :setup
|
84
|
+
define_method(:setup) do
|
85
|
+
begin
|
86
|
+
new_setup
|
87
|
+
ensure
|
88
|
+
user_setup
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
58
92
|
end
|
59
93
|
end
|
60
94
|
end
|
@@ -113,26 +147,21 @@ module Hardmock
|
|
113
147
|
@main_mock_control.verify
|
114
148
|
ensure
|
115
149
|
@main_mock_control.clear_expectations if @main_mock_control
|
116
|
-
Hardmock.restore_all_stubbed_methods
|
117
150
|
$main_mock_control = nil
|
151
|
+
reset_stubs
|
118
152
|
end
|
119
153
|
|
120
154
|
# Purge the main MockControl of all expectations, restore all concrete stubbed/mocked methods
|
121
155
|
def clear_expectations
|
122
156
|
@main_mock_control.clear_expectations if @main_mock_control
|
123
|
-
|
157
|
+
reset_stubs
|
124
158
|
$main_mock_control = nil
|
125
159
|
end
|
126
160
|
|
127
|
-
|
128
|
-
|
129
|
-
|
161
|
+
def reset_stubs
|
162
|
+
Hardmock.restore_all_replaced_methods
|
163
|
+
end
|
130
164
|
|
131
|
-
# def self.main_mock_control
|
132
|
-
# raise "No main mock control set yet... chickening out" if $main_mock_control.nil?
|
133
|
-
# $main_mock_control
|
134
|
-
# end
|
135
|
-
|
136
165
|
end
|
137
166
|
|
138
167
|
# Insert Hardmock functionality into the TestCase base class
|
data/lib/hardmock/stubbing.rb
CHANGED
@@ -23,6 +23,10 @@ class Object #:nodoc:#
|
|
23
23
|
meta_eval { define_method name, &blk }
|
24
24
|
end
|
25
25
|
|
26
|
+
# def meta_eval_string(str)
|
27
|
+
# metaclass.instance_eval(str)
|
28
|
+
# end
|
29
|
+
|
26
30
|
# Defines an instance method within a class
|
27
31
|
# def class_def(name, &blk) #:nodoc:#
|
28
32
|
# class_eval { define_method name, &blk }
|
@@ -59,47 +63,47 @@ module Hardmock
|
|
59
63
|
# Exists only for documentation
|
60
64
|
end
|
61
65
|
|
62
|
-
class
|
66
|
+
class ReplacedMethod
|
63
67
|
attr_reader :target, :method_name
|
64
68
|
|
65
69
|
def initialize(target, method_name)
|
66
70
|
@target = target
|
67
71
|
@method_name = method_name
|
68
72
|
|
69
|
-
Hardmock.
|
73
|
+
Hardmock.track_replaced_method self
|
70
74
|
end
|
75
|
+
end
|
71
76
|
|
77
|
+
class StubbedMethod < ReplacedMethod #:nodoc:#
|
72
78
|
def invoke(args)
|
79
|
+
raise @raises if @raises
|
73
80
|
@return_value
|
74
81
|
end
|
75
82
|
|
76
83
|
def returns(stubbed_return)
|
77
84
|
@return_value = stubbed_return
|
78
85
|
end
|
79
|
-
end
|
80
|
-
|
81
|
-
class MockedMethod < StubbedMethod #:nodoc:#
|
82
|
-
|
83
|
-
def initialize(target, method_name, mock)
|
84
|
-
super target,method_name
|
85
|
-
@mock = mock
|
86
|
-
end
|
87
86
|
|
88
|
-
def
|
89
|
-
|
87
|
+
def raises(err)
|
88
|
+
err = RuntimeError.new(err) unless err.kind_of?(Exception)
|
89
|
+
@raises = err
|
90
|
+
# puts "Setup to raise #{@raises}"
|
90
91
|
end
|
91
|
-
|
92
92
|
end
|
93
93
|
|
94
94
|
class ::Object
|
95
95
|
def stubs!(method_name)
|
96
96
|
_ensure_stubbable method_name
|
97
|
-
|
98
97
|
method_name = method_name.to_s
|
98
|
+
already_stubbed = Hardmock.has_replaced_method?(self, method_name)
|
99
|
+
|
99
100
|
stubbed_method = Hardmock::StubbedMethod.new(self, method_name)
|
100
101
|
|
101
|
-
|
102
|
-
|
102
|
+
|
103
|
+
unless _is_mock? or already_stubbed
|
104
|
+
meta_eval do
|
105
|
+
alias_method "_hardmock_original_#{method_name}".to_sym, method_name.to_sym
|
106
|
+
end
|
103
107
|
end
|
104
108
|
|
105
109
|
meta_def method_name do |*args|
|
@@ -110,26 +114,41 @@ module Hardmock
|
|
110
114
|
end
|
111
115
|
|
112
116
|
def expects!(method_name, *args, &block)
|
117
|
+
if self._is_mock?
|
118
|
+
raise Hardmock::StubbingError, "Cannot use 'expects!(:#{method_name})' on a Mock object; try 'expects' instead"
|
119
|
+
end
|
113
120
|
_ensure_stubbable method_name
|
114
121
|
|
115
122
|
method_name = method_name.to_s
|
116
123
|
|
117
124
|
if @_my_mock.nil?
|
118
125
|
@_my_mock = Mock.new(_my_name, $main_mock_control)
|
119
|
-
|
126
|
+
Hardmock::ReplacedMethod.new(self, method_name)
|
127
|
+
|
120
128
|
meta_eval do
|
121
129
|
alias_method "_hardmock_original_#{method_name}".to_sym, method_name.to_sym
|
122
130
|
end
|
123
|
-
|
124
|
-
|
131
|
+
|
132
|
+
begin
|
133
|
+
$method_text_temp = %{
|
134
|
+
def #{method_name}(*args,&block)
|
135
|
+
@_my_mock.__send__(:#{method_name}, *args, &block)
|
136
|
+
end
|
137
|
+
}
|
138
|
+
class << self
|
139
|
+
eval $method_text_temp
|
140
|
+
end
|
141
|
+
ensure
|
142
|
+
$method_text_temp = nil
|
125
143
|
end
|
144
|
+
|
126
145
|
end
|
127
146
|
|
128
147
|
return @_my_mock.expects(method_name, *args, &block)
|
129
148
|
end
|
130
149
|
|
131
150
|
def _ensure_stubbable(method_name)
|
132
|
-
unless self.respond_to?(method_name.to_sym)
|
151
|
+
unless self.respond_to?(method_name.to_sym) or self._is_mock?
|
133
152
|
msg = "Cannot stub non-existant "
|
134
153
|
if self.kind_of?(Class)
|
135
154
|
msg += "class method #{_my_name}."
|
@@ -141,6 +160,10 @@ module Hardmock
|
|
141
160
|
end
|
142
161
|
end
|
143
162
|
|
163
|
+
def _is_mock?
|
164
|
+
self.kind_of?(Mock)
|
165
|
+
end
|
166
|
+
|
144
167
|
def _my_name
|
145
168
|
self.kind_of?(Class) ? self.name : self.class.name
|
146
169
|
end
|
@@ -152,21 +175,31 @@ module Hardmock
|
|
152
175
|
end
|
153
176
|
|
154
177
|
class << self
|
155
|
-
def
|
156
|
-
|
178
|
+
def track_replaced_method(replaced_method)
|
179
|
+
all_replaced_methods << replaced_method
|
157
180
|
end
|
158
181
|
|
159
|
-
def
|
160
|
-
$
|
182
|
+
def all_replaced_methods
|
183
|
+
$all_replaced_methods ||= []
|
184
|
+
end
|
185
|
+
|
186
|
+
def has_replaced_method?(obj, method_name)
|
187
|
+
hits = all_replaced_methods.select do |replaced|
|
188
|
+
(replaced.target.object_id == obj.object_id) and (replaced.method_name.to_s == method_name.to_s)
|
189
|
+
end
|
190
|
+
return !hits.empty?
|
161
191
|
end
|
162
192
|
|
163
|
-
def
|
164
|
-
|
165
|
-
|
166
|
-
|
193
|
+
def restore_all_replaced_methods
|
194
|
+
all_replaced_methods.each do |replaced|
|
195
|
+
unless replaced.target._is_mock?
|
196
|
+
replaced.target.meta_eval do
|
197
|
+
alias_method replaced.method_name.to_sym, "_hardmock_original_#{replaced.method_name}".to_sym
|
198
|
+
end
|
199
|
+
replaced.target._clear_mock
|
167
200
|
end
|
168
|
-
sm.target._clear_mock
|
169
201
|
end
|
202
|
+
all_replaced_methods.clear
|
170
203
|
end
|
171
204
|
end
|
172
205
|
|
@@ -9,11 +9,9 @@ class HardmockTest < Test::Unit::TestCase
|
|
9
9
|
#
|
10
10
|
|
11
11
|
it "conveniently creates mocks using create_mock and create_mocks" do
|
12
|
-
assert_nil @main_mock_control, "@main_mock_control not expected yet"
|
13
12
|
|
14
13
|
h = create_mock :donkey
|
15
14
|
assert_equal [ :donkey ], h.keys
|
16
|
-
assert_not_nil @main_mock_control, "@main_mock_control should be here"
|
17
15
|
|
18
16
|
assert_mock_exists :donkey
|
19
17
|
assert_same @donkey, h[:donkey]
|
@@ -380,7 +378,7 @@ class HardmockTest < Test::Unit::TestCase
|
|
380
378
|
assert_equal "<Mock hay_bailer>", @hay_bailer.inspect, "Wrong output from 'inspect'"
|
381
379
|
end
|
382
380
|
|
383
|
-
it "raises
|
381
|
+
it "raises if prepare_hardmock_control is invoked after create_mocks, or more than once" do
|
384
382
|
create_mock :hi_there
|
385
383
|
create_mocks :another, :one
|
386
384
|
assert_error RuntimeError, /already setup/ do
|
@@ -8,7 +8,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
8
8
|
# TESTS
|
9
9
|
#
|
10
10
|
|
11
|
-
it "stubs a class method (and un-stubs after
|
11
|
+
it "stubs a class method (and un-stubs after reset_stubs)" do
|
12
12
|
assert_equal "stones and gravel", Concrete.pour
|
13
13
|
assert_equal "glug glug", Jug.pour
|
14
14
|
|
@@ -23,7 +23,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
23
23
|
|
24
24
|
assert_equal "For roads", Concrete.describe, "'describe' method broken"
|
25
25
|
|
26
|
-
|
26
|
+
reset_stubs
|
27
27
|
|
28
28
|
assert_equal "stones and gravel", Concrete.pour, "'pour' method not restored"
|
29
29
|
assert_equal "For roads", Concrete.describe, "'describe' method broken after verify"
|
@@ -39,7 +39,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
39
39
|
assert_equal "awful", Concrete.describe
|
40
40
|
assert_equal "milk", Jug.pour
|
41
41
|
|
42
|
-
|
42
|
+
reset_stubs
|
43
43
|
|
44
44
|
assert_equal "stones and gravel", Concrete.pour
|
45
45
|
assert_equal "For roads", Concrete.describe
|
@@ -53,7 +53,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
53
53
|
slab.stubs!(:hit).returns("slap")
|
54
54
|
assert_equal "slap", slab.hit, "'hit' not stubbed"
|
55
55
|
|
56
|
-
|
56
|
+
reset_stubs
|
57
57
|
|
58
58
|
assert_equal "bonk", slab.hit, "'hit' not restored"
|
59
59
|
end
|
@@ -70,7 +70,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
70
70
|
assert_equal "an instance", scrape.describe, "'describe' on 'scrape' instance broken"
|
71
71
|
assert_equal "For roads", Concrete.describe, "'describe' class method broken"
|
72
72
|
|
73
|
-
|
73
|
+
reset_stubs
|
74
74
|
|
75
75
|
assert_equal "an instance", slab.describe, "'describe' instance method not restored"
|
76
76
|
assert_equal "an instance", scrape.describe, "'describe' on 'scrape' instance broken after restore"
|
@@ -89,14 +89,47 @@ class StubbingTest < Test::Unit::TestCase
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
should "allow re-stubbing" do
|
93
|
+
Concrete.stubs!(:pour).returns("one")
|
94
|
+
assert_equal "one", Concrete.pour
|
95
|
+
|
96
|
+
Concrete.stubs!(:pour).raises("hell")
|
97
|
+
assert_error RuntimeError, /hell/ do
|
98
|
+
Concrete.pour
|
99
|
+
end
|
100
|
+
|
101
|
+
Concrete.stubs!(:pour).returns("two")
|
102
|
+
assert_equal "two", Concrete.pour
|
103
|
+
|
104
|
+
reset_stubs
|
105
|
+
|
106
|
+
assert_equal "stones and gravel", Concrete.pour
|
107
|
+
end
|
108
|
+
|
92
109
|
it "does nothing with a runtime block when simply stubbing" do
|
93
|
-
|
110
|
+
|
94
111
|
slab = Concrete.new
|
95
112
|
slab.stubs!(:hit) do |nothing|
|
96
113
|
raise "BOOOMM!"
|
97
114
|
end
|
98
115
|
slab.hit
|
99
|
-
|
116
|
+
reset_stubs
|
117
|
+
end
|
118
|
+
|
119
|
+
it "can raise errors from a stubbed method" do
|
120
|
+
|
121
|
+
Concrete.stubs!(:pour).raises(StandardError.new("no!"))
|
122
|
+
assert_error StandardError, /no!/ do
|
123
|
+
Concrete.pour
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it "provides string syntax for convenient raising of RuntimeErrors" do
|
128
|
+
|
129
|
+
Concrete.stubs!(:pour).raises("never!")
|
130
|
+
assert_error RuntimeError, /never!/ do
|
131
|
+
Concrete.pour
|
132
|
+
end
|
100
133
|
end
|
101
134
|
|
102
135
|
|
@@ -105,7 +138,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
105
138
|
#
|
106
139
|
|
107
140
|
it "mocks specific methods on existing classes, and returns the class method to normal after verification" do
|
108
|
-
|
141
|
+
|
109
142
|
assert_equal "stones and gravel", Concrete.pour, "Concrete.pour is already messed up"
|
110
143
|
|
111
144
|
Concrete.expects!(:pour).returns("ALIGATORS")
|
@@ -116,7 +149,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
116
149
|
end
|
117
150
|
|
118
151
|
it "flunks if expected class method is not invoked" do
|
119
|
-
|
152
|
+
|
120
153
|
Concrete.expects!(:pour).returns("ALIGATORS")
|
121
154
|
assert_error(Hardmock::VerifyError, /Concrete.pour/, /unmet expectations/i) do
|
122
155
|
verify_mocks
|
@@ -125,7 +158,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
125
158
|
end
|
126
159
|
|
127
160
|
it "supports all normal mock functionality for class methods" do
|
128
|
-
|
161
|
+
|
129
162
|
Concrete.expects!(:pour, "two tons").returns("mice")
|
130
163
|
Concrete.expects!(:pour, "three tons").returns("cats")
|
131
164
|
Concrete.expects!(:pour, "four tons").raises("Can't do it")
|
@@ -160,14 +193,14 @@ class StubbingTest < Test::Unit::TestCase
|
|
160
193
|
end
|
161
194
|
|
162
195
|
should "not allow mocking non-existant class methods" do
|
163
|
-
|
196
|
+
|
164
197
|
assert_error Hardmock::StubbingError, /non-existant/, /something/ do
|
165
198
|
Concrete.expects!(:something)
|
166
199
|
end
|
167
200
|
end
|
168
201
|
|
169
202
|
it "mocks specific methods on existing instances, then restore them after verify" do
|
170
|
-
|
203
|
+
|
171
204
|
slab = Concrete.new
|
172
205
|
assert_equal "bonk", slab.hit
|
173
206
|
|
@@ -179,7 +212,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
179
212
|
end
|
180
213
|
|
181
214
|
it "flunks if expected instance method is not invoked" do
|
182
|
-
|
215
|
+
|
183
216
|
slab = Concrete.new
|
184
217
|
slab.expects!(:hit)
|
185
218
|
|
@@ -190,7 +223,7 @@ class StubbingTest < Test::Unit::TestCase
|
|
190
223
|
end
|
191
224
|
|
192
225
|
it "supports all normal mock functionality for instance methods" do
|
193
|
-
|
226
|
+
|
194
227
|
slab = Concrete.new
|
195
228
|
|
196
229
|
slab.expects!(:hit, "soft").returns("hey")
|
@@ -233,14 +266,50 @@ class StubbingTest < Test::Unit::TestCase
|
|
233
266
|
end
|
234
267
|
|
235
268
|
should "not allow mocking non-existant instance methods" do
|
236
|
-
|
269
|
+
|
237
270
|
slab = Concrete.new
|
238
271
|
assert_error Hardmock::StubbingError, /non-existant/, /something/ do
|
239
272
|
slab.expects!(:something)
|
240
273
|
end
|
241
274
|
end
|
242
275
|
|
243
|
-
should "support expectations that deal with runtime blocks"
|
276
|
+
should "support concrete expectations that deal with runtime blocks" do
|
277
|
+
|
278
|
+
Concrete.expects!(:pour, "a lot") do |how_much, block|
|
279
|
+
assert_equal "a lot", how_much, "Wrong how_much arg"
|
280
|
+
assert_not_nil block, "nil runtime block"
|
281
|
+
assert_equal "the block value", block.call, "Wrong runtime block value"
|
282
|
+
end
|
283
|
+
|
284
|
+
Concrete.pour("a lot") do
|
285
|
+
"the block value"
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
it "can stub methods on mock objects" do
|
291
|
+
create_mock :horse
|
292
|
+
@horse.stubs!(:speak).returns("silence")
|
293
|
+
@horse.stubs!(:hello).returns("nothing")
|
294
|
+
@horse.expects(:canter).returns("clip clop")
|
295
|
+
|
296
|
+
assert_equal "silence", @horse.speak
|
297
|
+
assert_equal "clip clop", @horse.canter
|
298
|
+
assert_equal "silence", @horse.speak
|
299
|
+
assert_equal "silence", @horse.speak
|
300
|
+
assert_equal "nothing", @horse.hello
|
301
|
+
assert_equal "nothing", @horse.hello
|
302
|
+
|
303
|
+
verify_mocks
|
304
|
+
reset_stubs
|
305
|
+
end
|
306
|
+
|
307
|
+
it "will not allow expects! to be used on a mock object" do
|
308
|
+
create_mock :cow
|
309
|
+
assert_error Hardmock::StubbingError, /expects!/, /mock/i, /something/ do
|
310
|
+
@cow.expects!(:something)
|
311
|
+
end
|
312
|
+
end
|
244
313
|
|
245
314
|
#
|
246
315
|
# HELPERS
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: hardmock
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.3.
|
7
|
-
date: 2007-11-
|
6
|
+
version: 1.3.1
|
7
|
+
date: 2007-11-17 00:00:00 -05:00
|
8
8
|
summary: A strict, ordered, expectation-oriented mock object library.
|
9
9
|
require_paths:
|
10
10
|
- lib
|