hardmock 1.1.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/CHANGES +8 -0
- data/LICENSE +7 -0
- data/README +48 -0
- data/Rakefile +93 -0
- data/lib/hardmock.rb +634 -0
- data/lib/method_cleanout.rb +14 -0
- data/test/functional/assert_error_test.rb +52 -0
- data/test/functional/auto_verify_test.rb +192 -0
- data/test/functional/direct_mock_usage_test.rb +396 -0
- data/test/functional/hardmock_test.rb +371 -0
- data/test/test_helper.rb +23 -0
- data/test/unit/expectation_builder_test.rb +18 -0
- data/test/unit/expector_test.rb +54 -0
- data/test/unit/method_cleanout_test.rb +35 -0
- data/test/unit/mock_control_test.rb +172 -0
- data/test/unit/mock_test.rb +275 -0
- data/test/unit/simple_expectation_test.rb +345 -0
- data/test/unit/trapper_test.rb +60 -0
- data/test/unit/verify_error_test.rb +34 -0
- metadata +81 -0
@@ -0,0 +1,371 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
|
2
|
+
require 'hardmock'
|
3
|
+
class HardmockTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# HELPERS
|
13
|
+
#
|
14
|
+
|
15
|
+
def assert_mock_exists(name)
|
16
|
+
assert_not_nil @all_mocks, "@all_mocks not here yet"
|
17
|
+
mo = @all_mocks[name]
|
18
|
+
assert_not_nil mo, "Mock '#{name}' not in @all_mocks"
|
19
|
+
assert_kind_of Mock, mo, "Wrong type of object, wanted a Mock"
|
20
|
+
assert_equal name.to_s, mo._name, "Mock '#{name}' had wrong name"
|
21
|
+
ivar = self.instance_variable_get("@#{name}")
|
22
|
+
assert_not_nil ivar, "Mock '#{name}' not set as ivar"
|
23
|
+
assert_same mo, ivar, "Mock '#{name}' ivar not same as instance in @all_mocks"
|
24
|
+
assert_same @main_mock_control, mo._control, "Mock '#{name}' doesn't share the main mock control"
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# TESTS
|
29
|
+
#
|
30
|
+
|
31
|
+
def test_create_mock_and_create_mocks
|
32
|
+
assert_nil @main_mock_control, "@main_mock_control not expected yet"
|
33
|
+
|
34
|
+
h = create_mock :donkey
|
35
|
+
assert_equal [ :donkey ], h.keys
|
36
|
+
assert_not_nil @main_mock_control, "@main_mock_control should be here"
|
37
|
+
|
38
|
+
assert_mock_exists :donkey
|
39
|
+
assert_same @donkey, h[:donkey]
|
40
|
+
|
41
|
+
assert_equal [ :donkey ], @all_mocks.keys, "Wrong keyset for @all_mocks"
|
42
|
+
|
43
|
+
h2 = create_mocks :cat, 'dog' # symbol/string indifference at this level
|
44
|
+
assert_equal [:cat,:dog].to_set, h2.keys.to_set, "Wrong keyset for second hash"
|
45
|
+
assert_equal [:cat,:dog,:donkey].to_set, @all_mocks.keys.to_set, "@all_mocks wrong"
|
46
|
+
|
47
|
+
assert_mock_exists :cat
|
48
|
+
assert_same @cat, h2[:cat]
|
49
|
+
assert_mock_exists :dog
|
50
|
+
assert_same @dog, h2[:dog]
|
51
|
+
|
52
|
+
assert_mock_exists :donkey
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_expect
|
56
|
+
assert_nil @order, "Should be no @order yet"
|
57
|
+
create_mock :order
|
58
|
+
assert_not_nil @order, "@order should be built"
|
59
|
+
|
60
|
+
# Setup an expectation
|
61
|
+
@order.expects.update_stuff :key1 => 'val1', :key2 => 'val2'
|
62
|
+
|
63
|
+
# Use the mock
|
64
|
+
@order.update_stuff :key1 => 'val1', :key2 => 'val2'
|
65
|
+
|
66
|
+
# Verify
|
67
|
+
verify_mocks
|
68
|
+
|
69
|
+
# See that it's ok to do it again
|
70
|
+
verify_mocks
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_typical_multi_mock_use
|
74
|
+
create_mocks :order_builder, :order, :customer
|
75
|
+
|
76
|
+
@order_builder.expects.create_new_order.returns @order
|
77
|
+
@customer.expects.account_number.returns(1234)
|
78
|
+
@order.expects.account_no = 1234
|
79
|
+
@order.expects.save!
|
80
|
+
|
81
|
+
# Run "the code"
|
82
|
+
o = @order_builder.create_new_order
|
83
|
+
o.account_no = @customer.account_number
|
84
|
+
o.save!
|
85
|
+
|
86
|
+
verify_mocks
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_typical_multi_mock_use_out_of_order
|
90
|
+
create_mocks :order_builder, :order, :customer
|
91
|
+
|
92
|
+
@order_builder.expects.create_new_order.returns @order
|
93
|
+
@customer.expects.account_number.returns(1234)
|
94
|
+
@order.expects.account_no = 1234
|
95
|
+
@order.expects.save!
|
96
|
+
|
97
|
+
# Run "the code"
|
98
|
+
o = @order_builder.create_new_order
|
99
|
+
err = assert_raise ExpectationError do
|
100
|
+
o.save!
|
101
|
+
end
|
102
|
+
assert_match(/wrong object/i, err.message)
|
103
|
+
assert_match(/order.save!/i, err.message)
|
104
|
+
assert_match(/customer.account_number/i, err.message)
|
105
|
+
|
106
|
+
assert_error VerifyError, /unmet expectations/i do
|
107
|
+
verify_mocks
|
108
|
+
end
|
109
|
+
|
110
|
+
# Appease the verifier
|
111
|
+
@order.account_no = 1234
|
112
|
+
@order.save!
|
113
|
+
end
|
114
|
+
|
115
|
+
class UserPresenter
|
116
|
+
def initialize(args)
|
117
|
+
view = args[:view]
|
118
|
+
model = args[:model]
|
119
|
+
model.when :data_changes do
|
120
|
+
view.user_name = model.user_name
|
121
|
+
end
|
122
|
+
view.when :user_edited do
|
123
|
+
model.user_name = view.user_name
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_mvp_usage_pattern
|
129
|
+
mox = create_mocks :model, :view
|
130
|
+
|
131
|
+
data_change = @model.expects.when(:data_changes) { |evt,block| block }
|
132
|
+
user_edit = @view.expects.when(:user_edited) { |evt,block| block }
|
133
|
+
|
134
|
+
UserPresenter.new mox
|
135
|
+
|
136
|
+
# Expect user name transfer from model to view
|
137
|
+
@model.expects.user_name.returns 'Da Croz'
|
138
|
+
@view.expects.user_name = 'Da Croz'
|
139
|
+
# Trigger data change event in model
|
140
|
+
data_change.block_value.call
|
141
|
+
|
142
|
+
# Expect user name transfer from view to model
|
143
|
+
@view.expects.user_name.returns '6:8'
|
144
|
+
@model.expects.user_name = '6:8'
|
145
|
+
# Trigger edit event in view
|
146
|
+
user_edit.block_value.call
|
147
|
+
|
148
|
+
verify_mocks
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_verify_mocks_repeated_anger
|
152
|
+
mox = create_mocks :model, :view
|
153
|
+
data_change = @model.expects.when(:data_changes) { |evt,block| block }
|
154
|
+
user_edit = @view.expects.when(:user_edited) { |evt,block| block }
|
155
|
+
UserPresenter.new mox
|
156
|
+
|
157
|
+
# Expect user name transfer from model to view
|
158
|
+
@model.expects.user_name.returns 'Da Croz'
|
159
|
+
@view.expects.user_name = 'Da Croz'
|
160
|
+
|
161
|
+
assert_error ExpectationError, /model.monkey_wrench/i do
|
162
|
+
@model.monkey_wrench
|
163
|
+
end
|
164
|
+
|
165
|
+
# This should raise because of unmet expectations
|
166
|
+
assert_error VerifyError, /unmet expectations/i, /user_name/i do
|
167
|
+
verify_mocks
|
168
|
+
end
|
169
|
+
|
170
|
+
# See that the non-forced verification remains quiet
|
171
|
+
assert_nothing_raised VerifyError do
|
172
|
+
verify_mocks(false)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Finish meeting expectations and see good verification behavior
|
176
|
+
@view.user_name = "Da Croz"
|
177
|
+
verify_mocks
|
178
|
+
|
179
|
+
@model.expects.never_gonna_happen
|
180
|
+
|
181
|
+
assert_error VerifyError, /unmet expectations/i, /never_gonna_happen/i do
|
182
|
+
verify_mocks
|
183
|
+
end
|
184
|
+
|
185
|
+
# Appease the verifier
|
186
|
+
@model.never_gonna_happen
|
187
|
+
end
|
188
|
+
|
189
|
+
class UserPresenterBroken
|
190
|
+
def initialize(args)
|
191
|
+
view = args[:view]
|
192
|
+
model = args[:model]
|
193
|
+
model.when :data_changes do
|
194
|
+
view.user_name = model.user_name
|
195
|
+
end
|
196
|
+
# no view stuff, will break appropriately
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_mvp_usage_with_failures_in_constructor
|
201
|
+
mox = create_mocks :model, :view
|
202
|
+
|
203
|
+
data_change = @model.expects.when(:data_changes) { |evt,block| block }
|
204
|
+
user_edit = @view.expects.when(:user_edited) { |evt,block| block }
|
205
|
+
|
206
|
+
UserPresenterBroken.new mox
|
207
|
+
|
208
|
+
err = assert_raise VerifyError do
|
209
|
+
verify_mocks
|
210
|
+
end
|
211
|
+
assert_match(/unmet expectations/i, err.message)
|
212
|
+
assert_match(/view.when\(:user_edited\)/i, err.message)
|
213
|
+
|
214
|
+
assert_error VerifyError, /unmet expectations/i do
|
215
|
+
verify_mocks
|
216
|
+
end
|
217
|
+
|
218
|
+
# Appease the verifier
|
219
|
+
@view.when(:user_edited)
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_mvp_usage_pattern_with_convenience_trap
|
224
|
+
mox = create_mocks :model, :view
|
225
|
+
|
226
|
+
data_change = @model.trap.when(:data_changes)
|
227
|
+
user_edit = @view.trap.when(:user_edited)
|
228
|
+
|
229
|
+
UserPresenter.new mox
|
230
|
+
|
231
|
+
# Expect user name transfer from model to view
|
232
|
+
@model.expects.user_name.returns 'Da Croz'
|
233
|
+
@view.expects.user_name = 'Da Croz'
|
234
|
+
# Trigger data change event in model
|
235
|
+
data_change.trigger
|
236
|
+
|
237
|
+
# Expect user name transfer from view to model
|
238
|
+
@view.expects.user_name.returns '6:8'
|
239
|
+
@model.expects.user_name = '6:8'
|
240
|
+
# Trigger edit event in view
|
241
|
+
user_edit.trigger
|
242
|
+
|
243
|
+
verify_mocks
|
244
|
+
end
|
245
|
+
|
246
|
+
class Grinder
|
247
|
+
def initialize(objects)
|
248
|
+
@chute = objects[:chute]
|
249
|
+
@bucket = objects[:bucket]
|
250
|
+
@blade = objects[:blade]
|
251
|
+
end
|
252
|
+
|
253
|
+
def grind(slot)
|
254
|
+
@chute.each_bean(slot) do |bean|
|
255
|
+
@bucket << @blade.chop(bean)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_internal_iteration_usage
|
261
|
+
grinder = Grinder.new create_mocks(:blade, :chute, :bucket)
|
262
|
+
|
263
|
+
# Style 1: assertions on method args is done explicitly in block
|
264
|
+
@chute.expects.each_bean { |slot,block|
|
265
|
+
assert_equal :side_slot, slot, "Wrong slot"
|
266
|
+
block.call :bean1
|
267
|
+
block.call :bean2
|
268
|
+
}
|
269
|
+
|
270
|
+
@blade.expects.chop(:bean1).returns(:grounds1)
|
271
|
+
@bucket.expects('<<', :grounds1)
|
272
|
+
|
273
|
+
@blade.expects.chop(:bean2).returns(:grounds2)
|
274
|
+
@bucket.expects('<<', :grounds2)
|
275
|
+
|
276
|
+
# Run "the code"
|
277
|
+
grinder.grind(:side_slot)
|
278
|
+
|
279
|
+
verify_mocks
|
280
|
+
|
281
|
+
# Style 2: assertions on method arguments done implicitly in the expectation code
|
282
|
+
@chute.expects.each_bean(:main_slot) { |slot,block|
|
283
|
+
block.call :bean3
|
284
|
+
}
|
285
|
+
@blade.expects.chop(:bean3).returns(:grounds3)
|
286
|
+
@bucket.expects('<<', :grounds3)
|
287
|
+
grinder.grind :main_slot
|
288
|
+
verify_mocks
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_internal_iteration_using_yield
|
292
|
+
grinder = Grinder.new create_mocks(:blade, :chute, :bucket)
|
293
|
+
|
294
|
+
@chute.expects.each_bean(:side_slot).yields :bean1, :bean2
|
295
|
+
|
296
|
+
@blade.expects.chop(:bean1).returns(:grounds1)
|
297
|
+
@bucket.expects('<<', :grounds1)
|
298
|
+
|
299
|
+
@blade.expects.chop(:bean2).returns(:grounds2)
|
300
|
+
@bucket.expects('<<', :grounds2)
|
301
|
+
|
302
|
+
grinder.grind :side_slot
|
303
|
+
|
304
|
+
verify_mocks
|
305
|
+
end
|
306
|
+
|
307
|
+
class HurtLocker
|
308
|
+
attr_reader :caught
|
309
|
+
def initialize(opts)
|
310
|
+
@locker = opts[:locker]
|
311
|
+
@store = opts[:store]
|
312
|
+
end
|
313
|
+
|
314
|
+
def do_the_thing(area,data)
|
315
|
+
@locker.with_lock(area) do
|
316
|
+
@store.eat(data)
|
317
|
+
end
|
318
|
+
rescue => oops
|
319
|
+
@caught = oops
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def test_internal_locking_scenario
|
324
|
+
hurt = HurtLocker.new create_mocks(:locker, :store)
|
325
|
+
|
326
|
+
@locker.expects.with_lock(:main).yields
|
327
|
+
@store.expects.eat("some info")
|
328
|
+
|
329
|
+
hurt.do_the_thing(:main, "some info")
|
330
|
+
|
331
|
+
verify_mocks
|
332
|
+
end
|
333
|
+
|
334
|
+
def test_internal_locking_scenario_with_inner_error
|
335
|
+
hurt = HurtLocker.new create_mocks(:locker, :store)
|
336
|
+
err = StandardError.new('fmshooop')
|
337
|
+
@locker.expects.with_lock(:main).yields
|
338
|
+
@store.expects.eat("some info").raises(err)
|
339
|
+
|
340
|
+
hurt.do_the_thing(:main, "some info")
|
341
|
+
|
342
|
+
assert_same err, hurt.caught, "Expected that error to be handled internally"
|
343
|
+
verify_mocks
|
344
|
+
end
|
345
|
+
|
346
|
+
def test_returning_false_actually_returns_false_and_not_nil
|
347
|
+
create_mock :car
|
348
|
+
@car.expects.ignition_on?.returns(true)
|
349
|
+
assert_equal true, @car.ignition_on?, "Should be true"
|
350
|
+
@car.expects.ignition_on?.returns(false)
|
351
|
+
assert_equal false, @car.ignition_on?, "Should be false"
|
352
|
+
end
|
353
|
+
|
354
|
+
def test_should_raise_deprecation_error_when_old_expect_called
|
355
|
+
create_mock :foo
|
356
|
+
assert_error Hardmock::DeprecationError, /expect/,/expects/,/instead of/,/sorry/i do
|
357
|
+
@foo.expect.something
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def test_should_be_able_to_mock_methods_inherited_from_object
|
362
|
+
target_methods = %w|to_s inspect instance_eval instance_variables id clone display dup eql? ==|
|
363
|
+
create_mock :foo
|
364
|
+
target_methods.each do |m|
|
365
|
+
eval %{@foo.expects(m, "some stuff")}
|
366
|
+
eval %{@foo.#{m} "some stuff"}
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
end
|
371
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
here = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$: << here
|
3
|
+
|
4
|
+
require "#{here}/../config/environment"
|
5
|
+
require 'test/unit'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'logger'
|
8
|
+
require 'find'
|
9
|
+
require 'yaml'
|
10
|
+
require 'set'
|
11
|
+
require 'ostruct'
|
12
|
+
|
13
|
+
class Test::Unit::TestCase
|
14
|
+
include FileUtils
|
15
|
+
|
16
|
+
def poll(time_limit)
|
17
|
+
(time_limit * 10).to_i.times do
|
18
|
+
return true if yield
|
19
|
+
sleep 0.1
|
20
|
+
end
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
|
2
|
+
require 'hardmock'
|
3
|
+
|
4
|
+
class ExpectationBuilderTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_build_expectation
|
7
|
+
builder = ExpectationBuilder.new
|
8
|
+
|
9
|
+
ex = builder.build_expectation( :stuff => 'inside' )
|
10
|
+
assert_not_nil ex, "Didn't build an expectation"
|
11
|
+
assert_kind_of SimpleExpectation, ex, "Wrong type!"
|
12
|
+
|
13
|
+
# Shhhh... fragile, yes, whatever. The functional tests do the
|
14
|
+
# real testing of this anyway
|
15
|
+
assert_equal({:stuff => 'inside'}, ex.instance_variable_get('@options'), "Hash not sent to SimpleExpectation constructor")
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
|
2
|
+
require 'hardmock'
|
3
|
+
|
4
|
+
class ExpectorTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
class MyControl
|
7
|
+
attr_reader :added
|
8
|
+
def add_expectation(expectation)
|
9
|
+
@added ||= []
|
10
|
+
@added << expectation
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ExpBuilder
|
15
|
+
attr_reader :options
|
16
|
+
def build_expectation(options)
|
17
|
+
@options = options
|
18
|
+
"dummy expectation"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# TESTS
|
24
|
+
#
|
25
|
+
|
26
|
+
def test_method_missing
|
27
|
+
try_it_with 'wonder_bread'
|
28
|
+
try_it_with 'whatever'
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_methods_that_wont_trigger_method_missing
|
32
|
+
try_it_with 'instance_eval'
|
33
|
+
end
|
34
|
+
|
35
|
+
def try_it_with(method_name)
|
36
|
+
mock = Object.new
|
37
|
+
mock_control = MyControl.new
|
38
|
+
builder = ExpBuilder.new
|
39
|
+
|
40
|
+
exp = Expector.new(mock, mock_control, builder)
|
41
|
+
output = exp.send(method_name,:with, 1, 'sauce')
|
42
|
+
|
43
|
+
assert_same mock, builder.options[:mock]
|
44
|
+
assert_equal method_name, builder.options[:method].to_s
|
45
|
+
assert_equal [:with,1,'sauce'], builder.options[:arguments]
|
46
|
+
assert_nil builder.options[:block]
|
47
|
+
assert_equal [ "dummy expectation" ], mock_control.added,
|
48
|
+
"Wrong expectation added to control"
|
49
|
+
|
50
|
+
assert_equal "dummy expectation", output, "Expectation should have been returned"
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
|
2
|
+
require 'method_cleanout'
|
3
|
+
|
4
|
+
class MethodCleanoutTest < Test::Unit::TestCase
|
5
|
+
class Victim
|
6
|
+
OriginalMethods = instance_methods
|
7
|
+
include Hardmock::MethodCleanout
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@victim = Victim.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_should_remove_most_methods_from_a_class
|
15
|
+
expect_removed = Victim::OriginalMethods.reject { |m|
|
16
|
+
Hardmock::MethodCleanout::SACRED_METHODS.include?(m)
|
17
|
+
}
|
18
|
+
expect_removed.each do |m|
|
19
|
+
assert !@victim.respond_to?(m), "should not have method #{m}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_leave_the_sacred_methods_defined
|
24
|
+
Hardmock::MethodCleanout::SACRED_METHODS.each do |m|
|
25
|
+
assert @victim.respond_to?(m)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_should_include_certain_important_methods_in_the_sacred_methods_list
|
30
|
+
%w|__id__ __send__ send equal? respond_to? nil?|.each do |m|
|
31
|
+
assert Hardmock::MethodCleanout::SACRED_METHODS.include?(m), "important method #{m} is not included in SACRED_METHODS"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
|
2
|
+
require 'hardmock'
|
3
|
+
|
4
|
+
class MockControlTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@unmock = OpenStruct.new( :_name => 'fakemock' )
|
8
|
+
|
9
|
+
@control = MockControl.new
|
10
|
+
assert @control.happy?, "Control should start out happy"
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# HELPERS
|
18
|
+
#
|
19
|
+
|
20
|
+
class MyExp
|
21
|
+
attr_reader :mock, :mname, :args, :block
|
22
|
+
def apply_method_call(mock, mname, args, block)
|
23
|
+
@mock = mock
|
24
|
+
@mname = mname
|
25
|
+
@args = args
|
26
|
+
@block = block
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class BoomExp < MyExp
|
31
|
+
def apply_method_call(mock, mname, args, block)
|
32
|
+
super
|
33
|
+
raise "BOOM"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# TESTS
|
39
|
+
#
|
40
|
+
|
41
|
+
def test_add_exepectation_and_apply_method_call
|
42
|
+
e1 = MyExp.new
|
43
|
+
|
44
|
+
@control.add_expectation e1
|
45
|
+
assert !@control.happy?
|
46
|
+
|
47
|
+
@control.apply_method_call @unmock, 'some_func', [ 'the', :args ], nil
|
48
|
+
assert @control.happy?
|
49
|
+
|
50
|
+
assert_same @unmock, e1.mock, "Wrong mock"
|
51
|
+
assert_equal 'some_func', e1.mname, "Wrong method"
|
52
|
+
assert_equal [ 'the', :args ], e1.args, "Wrong args"
|
53
|
+
|
54
|
+
@control.verify
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_add_exepectation_and_apply_method_call_with_block
|
58
|
+
e1 = MyExp.new
|
59
|
+
|
60
|
+
@control.add_expectation e1
|
61
|
+
assert !@control.happy?
|
62
|
+
|
63
|
+
runtime_block = Proc.new { "hello" }
|
64
|
+
@control.apply_method_call @unmock, 'some_func', [ 'the', :args ], runtime_block
|
65
|
+
assert @control.happy?
|
66
|
+
|
67
|
+
assert_same @unmock, e1.mock, "Wrong mock"
|
68
|
+
assert_equal 'some_func', e1.mname, "Wrong method"
|
69
|
+
assert_equal [ 'the', :args ], e1.args, "Wrong args"
|
70
|
+
assert_equal "hello", e1.block.call, "Wrong block in expectation"
|
71
|
+
|
72
|
+
@control.verify
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_add_expectation_then_verify
|
76
|
+
e1 = MyExp.new
|
77
|
+
|
78
|
+
@control.add_expectation e1
|
79
|
+
assert !@control.happy?, "Shoudn't be happy"
|
80
|
+
err = assert_raise VerifyError do
|
81
|
+
@control.verify
|
82
|
+
end
|
83
|
+
assert_match(/unmet expectations/i, err.message)
|
84
|
+
|
85
|
+
@control.apply_method_call @unmock, 'some_func', [ 'the', :args ], nil
|
86
|
+
assert @control.happy?
|
87
|
+
|
88
|
+
assert_same @unmock, e1.mock, "Wrong mock"
|
89
|
+
assert_equal 'some_func', e1.mname, "Wrong method"
|
90
|
+
assert_equal [ 'the', :args ], e1.args, "Wrong args"
|
91
|
+
|
92
|
+
@control.verify
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_expectation_explosion
|
96
|
+
be1 = BoomExp.new
|
97
|
+
|
98
|
+
@control.add_expectation be1
|
99
|
+
|
100
|
+
err = assert_raise RuntimeError do
|
101
|
+
@control.apply_method_call @unmock, 'a func', [:arg], nil
|
102
|
+
end
|
103
|
+
assert_match(/BOOM/i, err.message)
|
104
|
+
|
105
|
+
assert_same @unmock, be1.mock
|
106
|
+
assert_equal 'a func', be1.mname
|
107
|
+
assert_equal [:arg], be1.args
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_disappointment_on_bad_verify
|
111
|
+
@control.add_expectation MyExp.new
|
112
|
+
assert !@control.happy?, "Shouldn't be happy"
|
113
|
+
assert !@control.disappointed?, "too early to be disappointed"
|
114
|
+
|
115
|
+
# See verify fails
|
116
|
+
err = assert_raise VerifyError do
|
117
|
+
@control.verify
|
118
|
+
end
|
119
|
+
assert_match(/unmet expectations/i, err.message)
|
120
|
+
|
121
|
+
assert !@control.happy?, "Still have unmet expectation"
|
122
|
+
assert @control.disappointed?, "We should be disappointed following that failure"
|
123
|
+
|
124
|
+
@control.apply_method_call @unmock, 'something', [], nil
|
125
|
+
assert @control.happy?, "Should be happy"
|
126
|
+
assert @control.disappointed?, "We should be skeptical"
|
127
|
+
|
128
|
+
@control.verify
|
129
|
+
|
130
|
+
assert !@control.disappointed?, "Should be non-disappointed"
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_disappointment_from_surprise_calls
|
134
|
+
assert @control.happy?, "Should be happy"
|
135
|
+
assert !@control.disappointed?, "too early to be disappointed"
|
136
|
+
|
137
|
+
# See verify fails
|
138
|
+
err = assert_raise ExpectationError do
|
139
|
+
@control.apply_method_call @unmock, "something", [], nil
|
140
|
+
end
|
141
|
+
assert_match(/surprise/i, err.message)
|
142
|
+
|
143
|
+
assert @control.happy?, "Happiness is an empty list of expectations"
|
144
|
+
assert @control.disappointed?, "We should be disappointed following that failure"
|
145
|
+
|
146
|
+
@control.verify
|
147
|
+
assert !@control.disappointed?, "Disappointment should be gone"
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_disappointment_from_bad_calls
|
151
|
+
be1 = BoomExp.new
|
152
|
+
assert !@control.disappointed?, "Shouldn't be disappointed"
|
153
|
+
@control.add_expectation be1
|
154
|
+
assert !@control.disappointed?, "Shouldn't be disappointed"
|
155
|
+
|
156
|
+
err = assert_raise RuntimeError do
|
157
|
+
@control.apply_method_call @unmock, 'a func', [:arg], nil
|
158
|
+
end
|
159
|
+
assert_match(/BOOM/i, err.message)
|
160
|
+
assert @control.disappointed?, "Should be disappointed"
|
161
|
+
|
162
|
+
assert_same @unmock, be1.mock
|
163
|
+
assert_equal 'a func', be1.mname
|
164
|
+
assert_equal [:arg], be1.args
|
165
|
+
|
166
|
+
assert @control.happy?, "Happiness is an empty list of expectations"
|
167
|
+
@control.verify
|
168
|
+
assert !@control.disappointed?, "Disappointment should be gone"
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
end
|