hardmock 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/hardmock.rb ADDED
@@ -0,0 +1,634 @@
1
+ require 'method_cleanout'
2
+
3
+ module Hardmock
4
+
5
+ # Setup auto mock verification on teardown, being careful not to interfere
6
+ # with inherited, pre-mixed or post-added user teardowns.
7
+ def self.included(base) #:nodoc:#
8
+ base.class_eval do
9
+ # Core of our actual teardown behavior
10
+ def hardmock_teardown
11
+ verify_mocks
12
+ end
13
+
14
+ # disable until later:
15
+ def self.method_added(symbol) #:nodoc:
16
+ end
17
+
18
+ if method_defined?(:teardown) then
19
+ # Wrap existing teardown
20
+ alias_method :old_teardown, :teardown
21
+ define_method(:new_teardown) do
22
+ begin
23
+ hardmock_teardown
24
+ ensure
25
+ old_teardown
26
+ end
27
+ end
28
+ else
29
+ # We don't need to account for previous teardown
30
+ define_method(:new_teardown) do
31
+ hardmock_teardown
32
+ end
33
+ end
34
+ alias_method :teardown, :new_teardown
35
+
36
+ def self.method_added(method) #:nodoc:
37
+ case method
38
+ when :teardown
39
+ unless method_defined?(:user_teardown)
40
+ alias_method :user_teardown, :teardown
41
+ define_method(:teardown) do
42
+ begin
43
+ new_teardown
44
+ ensure
45
+ user_teardown
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ # Create one or more new Mock instances in your test suite.
55
+ # Once created, the Mocks are accessible as instance variables in your test.
56
+ # Newly built Mocks are added to the full set of Mocks for this test, which will
57
+ # be verified when you call verify_mocks.
58
+ #
59
+ # create_mocks :donkey, :cat # Your test now has @donkey and @cat
60
+ # create_mock :dog # Test now has @donkey, @cat and @dog
61
+ #
62
+ # The first call returned a hash { :donkey => @donkey, :cat => @cat }
63
+ # and the second call returned { :dog => @dog }
64
+ #
65
+ # For more info on how to use your mocks, see Mock and SimpleExpectation
66
+ #
67
+ def create_mocks(*mock_names)
68
+ @main_mock_control ||= MockControl.new
69
+
70
+ mocks = {}
71
+ mock_names.each do |mock_name|
72
+ mock_name = mock_name.to_s
73
+ mock_object = Mock.new(mock_name, @main_mock_control)
74
+ mocks[mock_name.to_sym] = mock_object
75
+ self.instance_variable_set "@#{mock_name}", mock_object
76
+ end
77
+ @all_mocks ||= {}
78
+ @all_mocks.merge! mocks
79
+
80
+ return mocks.clone
81
+ end
82
+
83
+ alias :create_mock :create_mocks
84
+
85
+ # Ensures that all expectations have been met. If not, VerifyException is
86
+ # raised.
87
+ #
88
+ # <b>You normally won't need to call this yourself.</b> Within Test::Unit::TestCase, this will be done automatically at teardown time.
89
+ #
90
+ # * +force+ -- if +false+, and a VerifyError or ExpectationError has already occurred, this method will not raise. This is to help you suppress repeated errors when if you're calling #verify_mocks in the teardown method of your test suite. BE WARNED - only use this if you're sure you aren't obscuring useful information. Eg, if your code handles exceptions internally, and an ExpectationError gets gobbled up by your +rescue+ block, the cause of failure for your test may be hidden from you. For this reason, #verify_mocks defaults to force=true as of Hardmock 1.0.1
91
+ def verify_mocks(force=true)
92
+ return unless @main_mock_control
93
+ return if @main_mock_control.disappointed? and !force
94
+ @main_mock_control.verify
95
+ end
96
+
97
+ module Utils #:nodoc:
98
+ def fmt_call(mock,mname,args)
99
+ arg_string = args.map { |a| a.inspect }.join(', ')
100
+ call_text = "#{mock._name}.#{mname}(#{arg_string})"
101
+ end
102
+ end
103
+
104
+ # Mock is used to set expectations in your test. Most of the time you'll use
105
+ # <tt>#expects</tt> to create expectations.
106
+ #
107
+ # Aside from the scant few control methods (like +expects+, +trap+ and +_verify+)
108
+ # all calls made on a Mock instance will be immediately applied to the internal
109
+ # expectation mechanism.
110
+ #
111
+ # * If the method call was expected and all the parameters match properly, execution continues
112
+ # * If the expectation was configured with an expectation block, the block is invoked
113
+ # * If the expectation was set up to raise an error, the error is raised now
114
+ # * If the expectation was set up to return a value, it is returned
115
+ # * If the method call was _not_ expected, or the parameter values are wrong, an ExpectationError is raised.
116
+ class Mock
117
+ include MethodCleanout
118
+
119
+ # Create a new Mock instance with a name and a MockControl to support it.
120
+ # If not given, a MockControl is made implicitly for this Mock alone; this means
121
+ # expectations for this mock are not tied to other expectations in your test.
122
+ #
123
+ # It's not recommended to use a Mock directly; see Hardmock and
124
+ # Hardmock#create_mocks for the more wholistic approach.
125
+ def initialize(name, mock_control=nil)
126
+ @name = name
127
+ @control = mock_control || MockControl.new
128
+ @expectation_builder = ExpectationBuilder.new
129
+ end
130
+
131
+ # Begin declaring an expectation for this Mock.
132
+ #
133
+ # == Simple Examples
134
+ # Expect the +customer+ to be queried for +account+, and return <tt>"The
135
+ # Account"</tt>:
136
+ # @customer.expects.account.returns "The Account"
137
+ #
138
+ # Expect the +withdraw+ method to be called, and raise an exception when it
139
+ # is (see SimpleExpectation#raises for more info):
140
+ # @cash_machine.expects.withdraw(20,:dollars).raises("not enough money")
141
+ #
142
+ # Expect +customer+ to have its +user_name+ set
143
+ # @customer.expects.user_name = 'Big Boss'
144
+ #
145
+ # Expect +customer+ to have its +user_name+ set, and raise a RuntimeException when
146
+ # that happens:
147
+ # @customer.expects('user_name=', "Big Boss").raises "lost connection"
148
+ #
149
+ # Expect +evaluate+ to be passed a block, and when that happens, pass a value
150
+ # to the block (see SimpleExpectation#yields for more info):
151
+ # @cruncher.expects.evaluate.yields("some data").returns("some results")
152
+ #
153
+ #
154
+ # == Expectation Blocks
155
+ # To do special handling of expected method calls when they occur, you
156
+ # may pass a block to your expectation, like:
157
+ # @page_scraper.expects.handle_content do |address,request,status|
158
+ # assert_not_nil address, "Can't abide nil addresses"
159
+ # assert_equal "http-get", request.method, "Can only handle GET"
160
+ # assert status > 200 and status < 300, status, "Failed status"
161
+ # "Simulated results #{request.content.downcase}"
162
+ # end
163
+ # In this example, when <tt>page_scraper.handle_content</tt> is called, its
164
+ # three arguments are passed to the <i>expectation block</i> and evaluated
165
+ # using the above assertions. The last value in the block will be used
166
+ # as the return value for +handle_content+
167
+ #
168
+ # You may specify arguments to the expected method call, just like any normal
169
+ # expectation, and those arguments will be pre-validated before being passed
170
+ # to the expectation block. This is useful when you know all of the
171
+ # expected values but still need to do something programmatic.
172
+ #
173
+ # If the method being invoked on the mock accepts a block, that block will be
174
+ # passed to your expectation block as the last (or only) argument. Eg, the
175
+ # convenience method +yields+ can be replaced with the more explicit:
176
+ # @cruncher.expects.evaluate do |block|
177
+ # block.call "some data"
178
+ # "some results"
179
+ # end
180
+ #
181
+ # The result value of the expectation block becomes the return value for the
182
+ # expected method call. This can be overidden by using the +returns+ method:
183
+ # @cruncher.expects.evaluate do |block|
184
+ # block.call "some data"
185
+ # "some results"
186
+ # end.returns("the actual value")
187
+ #
188
+ # <b>Additionally</b>, the resulting value of the expectation block is stored
189
+ # in the +block_value+ field on the expectation. If you've saved a reference
190
+ # to your expectation, you may retrieve the block value once the expectation
191
+ # has been met.
192
+ #
193
+ # evaluation_event = @cruncher.expects.evaluate do |block|
194
+ # block.call "some data"
195
+ # "some results"
196
+ # end.returns("the actual value")
197
+ #
198
+ # result = @cruncher.evaluate do |input|
199
+ # puts input # => 'some data'
200
+ # end
201
+ # # result is 'the actual value'
202
+ #
203
+ # evaluation_event.block_value # => 'some results'
204
+ #
205
+ def expects(*args, &block)
206
+ expector = Expector.new(self,@control,@expectation_builder)
207
+ # If there are no args, we return the Expector
208
+ return expector if args.empty?
209
+ # If there ARE args, we set up the expectation right here and return it
210
+ expector.send(args.shift.to_sym, *args, &block)
211
+ end
212
+
213
+ def expect(*args, &block) #:nodoc:
214
+ raise DeprecationError.new("Please use 'expects' instead of 'expect'. Sorry about the inconvenience.")
215
+ end
216
+
217
+ # Special-case convenience: #trap sets up an expectation for a method
218
+ # that will take a block. That block, when sent to the expected method, will
219
+ # be trapped and stored in the expectation's +block_value+ field.
220
+ # The SimpleExpectation#trigger method may then be used to invoke that block.
221
+ #
222
+ # Like +expects+, the +trap+ mechanism can be followed by +raises+ or +returns+.
223
+ #
224
+ # _Unlike_ +expects+, you may not use an expectation block with +trap+. If
225
+ # the expected method takes arguments in addition to the block, they must
226
+ # be specified in the arguments to the +trap+ call itself.
227
+ #
228
+ # == Example
229
+ #
230
+ # create_mocks :address_book, :editor_form
231
+ #
232
+ # # Expect a subscription on the :person_added event for @address_book:
233
+ # person_event = @address_book.trap.subscribe(:person_added)
234
+ #
235
+ # # The runtime code would look like:
236
+ # @address_book.subscribe :person_added do |person_name|
237
+ # @editor_form.name = person_name
238
+ # end
239
+ #
240
+ # # At this point, the expectation for 'subscribe' is met and the
241
+ # # block has been captured. But we're not done:
242
+ # @editor_form.expects.name = "David"
243
+ #
244
+ # # Now invoke the block we trapped earlier:
245
+ # person_event.trigger "David"
246
+ #
247
+ # verify_mocks
248
+ def trap(*args)
249
+ Trapper.new(self,@control,ExpectationBuilder.new)
250
+ end
251
+
252
+ def method_missing(mname,*args) #:nodoc:
253
+ block = nil
254
+ block = Proc.new if block_given?
255
+ @control.apply_method_call(self,mname,args,block)
256
+ end
257
+
258
+
259
+ def _control #:nodoc:
260
+ @control
261
+ end
262
+
263
+ def _name #:nodoc:
264
+ @name
265
+ end
266
+
267
+ # Verify that all expectations are fulfilled. NOTE: this method triggers
268
+ # validation on the _control_ for this mock, so all Mocks that share the
269
+ # MockControl with this instance will be included in the verification.
270
+ #
271
+ # <b>Only use this method if you are managing your own Mocks and their controls.</b>
272
+ #
273
+ # Normal usage of Hardmock doesn't require you to call this; let
274
+ # Hardmock#verify_mocks do it for you.
275
+ def _verify
276
+ @control.verify
277
+ end
278
+ end
279
+
280
+ class Expector #:nodoc:
281
+ include MethodCleanout
282
+
283
+ def initialize(mock,mock_control,expectation_builder)
284
+ @mock = mock
285
+ @mock_control = mock_control
286
+ @expectation_builder = expectation_builder
287
+ end
288
+
289
+ def method_missing(mname, *args, &block)
290
+ expectation = @expectation_builder.build_expectation(
291
+ :mock => @mock,
292
+ :method => mname,
293
+ :arguments => args,
294
+ :block => block)
295
+
296
+ @mock_control.add_expectation expectation
297
+ expectation
298
+ end
299
+ end
300
+
301
+ class Trapper #:nodoc:
302
+ include MethodCleanout
303
+
304
+ def initialize(mock,mock_control,expectation_builder)
305
+ @mock = mock
306
+ @mock_control = mock_control
307
+ @expectation_builder = expectation_builder
308
+ end
309
+
310
+ def method_missing(mname, *args)
311
+ if block_given?
312
+ raise ExpectationError.new("Don't pass blocks when using 'trap' (setting exepectations for '#{mname}')")
313
+ end
314
+
315
+ the_block = lambda { |target_block| target_block }
316
+ expectation = @expectation_builder.build_expectation(
317
+ :mock => @mock,
318
+ :method => mname,
319
+ :arguments => args,
320
+ :suppress_arguments_to_block => true,
321
+ :block => the_block)
322
+
323
+ @mock_control.add_expectation expectation
324
+ expectation
325
+ end
326
+ end
327
+
328
+ class ExpectationBuilder #:nodoc:
329
+ def build_expectation(options)
330
+ SimpleExpectation.new(options)
331
+ end
332
+ end
333
+
334
+ class SimpleExpectation
335
+ include Utils
336
+ attr_reader :block_value
337
+
338
+ def initialize(options) #:nodoc:
339
+ @options = options
340
+ end
341
+
342
+ def apply_method_call(mock,mname,args,block) #:nodoc:
343
+ unless @options[:mock].equal?(mock)
344
+ raise anger("Wrong object", mock,mname,args)
345
+ end
346
+ unless @options[:method] == mname
347
+ raise anger("Wrong method",mock,mname,args)
348
+ end
349
+
350
+ # Tester-defined block to invoke at method-call-time:
351
+ expectation_block = @options[:block]
352
+
353
+ expected_args = @options[:arguments]
354
+ # if we have a block, we can skip the argument check if none were specified
355
+ unless (expected_args.nil? || expected_args.empty?) && expectation_block && !@options[:suppress_arguments_to_block]
356
+ unless expected_args == args
357
+ raise anger("Wrong arguments",mock,mname,args)
358
+ end
359
+ end
360
+
361
+ relayed_args = args.dup
362
+ if block
363
+ if expectation_block.nil?
364
+ # Can't handle a runtime block without an expectation block
365
+ raise ExpectationError.new("Unexpected block provided to #{to_s}")
366
+ else
367
+ # Runtime blocks are passed as final argument to the expectation block
368
+ unless @options[:suppress_arguments_to_block]
369
+ relayed_args << block
370
+ else
371
+ # Arguments suppressed; send only the block
372
+ relayed_args = [block]
373
+ end
374
+ end
375
+ end
376
+
377
+ # Run the expectation block:
378
+ @block_value = expectation_block.call(*relayed_args) if expectation_block
379
+
380
+ raise @options[:raises] unless @options[:raises].nil?
381
+
382
+ return_value = @options[:returns]
383
+ if return_value.nil?
384
+ return @block_value
385
+ else
386
+ return return_value
387
+ end
388
+ end
389
+
390
+ # Set the return value for an expected method call.
391
+ # Eg,
392
+ # @cash_machine.expects.withdraw(20,:dollars).returns(20.00)
393
+ def returns(val)
394
+ @options[:returns] = val
395
+ self
396
+ end
397
+
398
+ # Rig an expected method to raise an exception when the mock is invoked.
399
+ #
400
+ # Eg,
401
+ # @cash_machine.expects.withdraw(20,:dollars).raises "Insufficient funds"
402
+ #
403
+ # The argument can be:
404
+ # * an Exception -- will be used directly
405
+ # * a String -- will be used as the message for a RuntimeError
406
+ # * nothing -- RuntimeError.new("An Error") will be raised
407
+ def raises(err=nil)
408
+ case err
409
+ when Exception
410
+ @options[:raises] = err
411
+ when String
412
+ @options[:raises] = RuntimeError.new(err)
413
+ else
414
+ @options[:raises] = RuntimeError.new("An Error")
415
+ end
416
+ self
417
+ end
418
+
419
+ # Convenience method: assumes +block_value+ is set, and is set to a Proc
420
+ # (or anything that responds to 'call')
421
+ #
422
+ # light_event = @traffic_light.trap.subscribe(:light_changes)
423
+ #
424
+ # # This code will meet the expectation:
425
+ # @traffic_light.subscribe :light_changes do |color|
426
+ # puts color
427
+ # end
428
+ #
429
+ # The color-handling block is now stored in <tt>light_event.block_value</tt>
430
+ #
431
+ # The block can be invoked like this:
432
+ #
433
+ # light_event.trigger :red
434
+ #
435
+ # See Mock#trap and Mock#expects for information on using expectation objects
436
+ # after they are set.
437
+ #
438
+ def trigger(*block_arguments)
439
+ unless block_value
440
+ raise ExpectationError.new("No block value is currently set for expectation #{to_s}")
441
+ end
442
+ unless block_value.respond_to?(:call)
443
+ raise ExpectationError.new("Can't apply trigger to #{block_value} for expectation #{to_s}")
444
+ end
445
+ block_value.call *block_arguments
446
+ end
447
+
448
+ # Used when an expected method accepts a block at runtime.
449
+ # When the expected method is invoked, the block passed to
450
+ # that method will be invoked as well.
451
+ #
452
+ # NOTE: ExpectationError will be thrown upon running the expected method
453
+ # if the arguments you set up in +yields+ do not properly match up with
454
+ # the actual block that ends up getting passed.
455
+ #
456
+ # == Examples
457
+ # <b>Single invocation</b>: The block passed to +lock_down+ gets invoked
458
+ # once with no arguments:
459
+ #
460
+ # @safe_zone.expects.lock_down.yields
461
+ #
462
+ # # (works on code that looks like:)
463
+ # @safe_zone.lock_down do
464
+ # # ... this block invoked once
465
+ # end
466
+ #
467
+ # <b>Multi-parameter blocks:</b> The block passed to +each_item+ gets
468
+ # invoked twice, with <tt>:item1</tt> the first time, and with
469
+ # <tt>:item2</tt> the second time:
470
+ #
471
+ # @fruit_basket.expects.each_with_index.yields [:apple,1], [:orange,2]
472
+ #
473
+ # # (works on code that looks like:)
474
+ # @fruit_basket.each_with_index do |fruit,index|
475
+ # # ... this block invoked with fruit=:apple, index=1,
476
+ # # ... and then with fruit=:orange, index=2
477
+ # end
478
+ #
479
+ # <b>Arrays can be passed as arguments too</b>... if the block
480
+ # takes a single argument and you want to pass a series of arrays into it,
481
+ # that will work as well:
482
+ #
483
+ # @list_provider.expects.each_list.yields [1,2,3], [4,5,6]
484
+ #
485
+ # # (works on code that looks like:)
486
+ # @list_provider.each_list do |list|
487
+ # # ... list is [1,2,3] the first time
488
+ # # ... list is [4,5,6] the second time
489
+ # end
490
+ #
491
+ # <b>Return value</b>: You can set the return value for the method that
492
+ # accepts the block like so:
493
+ #
494
+ # @cruncher.expects.do_things.yields(:bean1,:bean2).returns("The Results")
495
+ #
496
+ # <b>Raising errors</b>: You can set the raised exception for the method that
497
+ # accepts the block. NOTE: the error will be raised _after_ the block has
498
+ # been invoked.
499
+ #
500
+ # # :bean1 and :bean2 will be passed to the block, then an error is raised:
501
+ # @cruncher.expects.do_things.yields(:bean1,:bean2).raises("Too crunchy")
502
+ #
503
+ def yields(*items)
504
+ @options[:suppress_arguments_to_block] = true
505
+ if items.empty?
506
+ # Yield once
507
+ @options[:block] = lambda do |block|
508
+ if block.arity != 0 and block.arity != -1
509
+ raise ExpectationError.new("Can't pass #{item.inspect} to block with arity #{block.arity} to <#{to_s}>")
510
+ end
511
+ block.call
512
+ end
513
+ else
514
+ # Yield one or more specific items
515
+ @options[:block] = lambda do |block|
516
+ items.each do |item|
517
+ if item.kind_of?(Array)
518
+ if block.arity == item.size
519
+ # Unfold the array into the block's arguments:
520
+ block.call *item
521
+ elsif block.arity == 1
522
+ # Just pass the array in
523
+ block.call item
524
+ else
525
+ # Size mismatch
526
+ raise ExpectationError.new("Can't pass #{item.inspect} to block with arity #{block.arity} to <#{to_s}>")
527
+ end
528
+ else
529
+ if block.arity != 1
530
+ # Size mismatch
531
+ raise ExpectationError.new("Can't pass #{item.inspect} to block with arity #{block.arity} to <#{to_s}>")
532
+ end
533
+ block.call item
534
+ end
535
+ end
536
+ end
537
+ end
538
+ self
539
+ end
540
+
541
+ def to_s # :nodoc:
542
+ fmt_call(@options[:mock],@options[:method],@options[:arguments])
543
+ end
544
+
545
+ private
546
+ def anger(msg, mock,mname,args)
547
+ ExpectationError.new("#{msg}: expected call <#{to_s}> but was <#{fmt_call(mock,mname,args)}>")
548
+ end
549
+ end
550
+
551
+ class MockControl #:nodoc:
552
+ include Utils
553
+ attr_accessor :name
554
+
555
+ def initialize
556
+ @expectations = []
557
+ @disappointed = false
558
+ end
559
+
560
+ def happy?
561
+ @expectations.empty?
562
+ end
563
+
564
+ def disappointed?
565
+ @disappointed
566
+ end
567
+
568
+ def add_expectation(expectation)
569
+ @expectations << expectation
570
+ end
571
+
572
+ def apply_method_call(mock,mname,args,block)
573
+ # Are we even expecting any sort of call?
574
+ if happy?
575
+ @disappointed = true
576
+ raise ExpectationError.new("Surprise call to #{fmt_call(mock,mname,args)}")
577
+ end
578
+
579
+ begin
580
+ @expectations.shift.apply_method_call(mock,mname,args,block)
581
+ rescue Exception => ouch
582
+ @disappointed = true
583
+ raise ouch
584
+ end
585
+ end
586
+
587
+ def verify
588
+ @disappointed = !happy?
589
+ raise VerifyError.new("Unmet expectations", @expectations) unless happy?
590
+ end
591
+ end
592
+
593
+ # Raised when:
594
+ # * Unexpected method is called on a mock object
595
+ # * Bad arguments passed to an expected call
596
+ class ExpectationError < StandardError; end
597
+
598
+ # Raised for methods that should no longer be called. Hopefully, the exception message contains helpful alternatives.
599
+ class DeprecationError < StandardError; end
600
+
601
+ # Raised when it is discovered that an expected method call was never made.
602
+ class VerifyError < StandardError
603
+ def initialize(msg,unmet_expectations)
604
+ super("#{msg}:" + unmet_expectations.map { |ex| "\n * #{ex.to_s}" }.join)
605
+ end
606
+ end
607
+
608
+ # A better 'assert_raise'. +patterns+ can be one or more Regexps, or a literal String that
609
+ # must match the entire error message.
610
+ def assert_error(err_type,*patterns,&block)
611
+ assert_not_nil block, "assert_error requires a block"
612
+ assert((err_type and err_type.kind_of?(Class)), "First argument to assert_error has to be an error type")
613
+ err = assert_raise(err_type) do
614
+ block.call
615
+ end
616
+ patterns.each do |pattern|
617
+ case pattern
618
+ when Regexp
619
+ assert_match(pattern, err.message)
620
+ else
621
+ assert_equal pattern, err.message
622
+ end
623
+ end
624
+ end
625
+
626
+ end
627
+
628
+ # Insert Hardmock functionality into the TestCase base class
629
+ require 'test/unit/testcase'
630
+ unless Test::Unit::TestCase.instance_methods.include?('hardmock_teardown')
631
+ class Test::Unit::TestCase
632
+ include Hardmock
633
+ end
634
+ end
@@ -0,0 +1,14 @@
1
+
2
+ module Hardmock #:nodoc:
3
+ module MethodCleanout #:nodoc:
4
+ SACRED_METHODS = %w|__id__ __send__ equal? object_id send nil? class kind_of? respond_to?|
5
+
6
+ def self.included(base) #:nodoc:
7
+ base.class_eval do
8
+ instance_methods.each { |m|
9
+ undef_method m unless SACRED_METHODS.include?(m.to_s)
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
+ require 'hardmock'
3
+
4
+ class AssertErrorTest < Test::Unit::TestCase
5
+
6
+ def test_assert_error
7
+ assert_error RuntimeError, "Too funky" do
8
+ raise RuntimeError.new("Too funky")
9
+ end
10
+ end
11
+
12
+ def test_assert_error_message_doesnt_match
13
+ err = assert_raise Test::Unit::AssertionFailedError do
14
+ assert_error RuntimeError, "not good" do
15
+ raise RuntimeError.new("Too funky")
16
+ end
17
+ end
18
+ assert_match(/not good/i, err.message)
19
+ assert_match(/too funky/i, err.message)
20
+ end
21
+
22
+ def test_assert_error_type_doesnt_match
23
+ err = assert_raise Test::Unit::AssertionFailedError do
24
+ assert_error StandardError, "Too funky" do
25
+ raise RuntimeError.new("Too funky")
26
+ end
27
+ end
28
+ assert_match(/StandardError/i, err.message)
29
+ assert_match(/RuntimeError/i, err.message)
30
+ end
31
+
32
+ def test_assert_error_regexp_matching
33
+ assert_error StandardError, /too/i, /funky/i do
34
+ raise StandardError.new("Too funky")
35
+ end
36
+ end
37
+
38
+ def test_assert_error_regexp_matching_fails
39
+ err = assert_raise Test::Unit::AssertionFailedError do
40
+ assert_error StandardError, /way/i, /too/i, /funky/i do
41
+ raise StandardError.new("Too funky")
42
+ end
43
+ end
44
+ assert_match(/way/i, err.message)
45
+ end
46
+
47
+ def test_assert_error_no_matchine
48
+ assert_error StandardError do
49
+ raise StandardError.new("ooof")
50
+ end
51
+ end
52
+ end