mocha 0.5.5 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/Rakefile +3 -1
  2. data/examples/misc.rb +44 -36
  3. data/examples/stubba.rb +1 -1
  4. data/lib/mocha/auto_verify.rb +38 -31
  5. data/lib/mocha/central.rb +1 -1
  6. data/lib/mocha/class_method.rb +5 -1
  7. data/lib/mocha/expectation.rb +63 -61
  8. data/lib/mocha/expectation_error.rb +9 -0
  9. data/lib/mocha/expectation_list.rb +11 -10
  10. data/lib/mocha/method_matcher.rb +21 -0
  11. data/lib/mocha/missing_expectation.rb +5 -15
  12. data/lib/mocha/mock.rb +28 -26
  13. data/lib/mocha/parameter_matchers.rb +17 -1
  14. data/lib/mocha/parameter_matchers/all_of.rb +6 -3
  15. data/lib/mocha/parameter_matchers/any_of.rb +6 -3
  16. data/lib/mocha/parameter_matchers/any_parameters.rb +40 -0
  17. data/lib/mocha/parameter_matchers/anything.rb +5 -2
  18. data/lib/mocha/parameter_matchers/base.rb +15 -0
  19. data/lib/mocha/parameter_matchers/equals.rb +42 -0
  20. data/lib/mocha/parameter_matchers/has_entries.rb +42 -0
  21. data/lib/mocha/parameter_matchers/has_entry.rb +20 -4
  22. data/lib/mocha/parameter_matchers/has_key.rb +5 -2
  23. data/lib/mocha/parameter_matchers/has_value.rb +5 -2
  24. data/lib/mocha/parameter_matchers/includes.rb +5 -2
  25. data/lib/mocha/parameter_matchers/instance_of.rb +5 -2
  26. data/lib/mocha/parameter_matchers/is_a.rb +42 -0
  27. data/lib/mocha/parameter_matchers/kind_of.rb +5 -2
  28. data/lib/mocha/parameter_matchers/not.rb +42 -0
  29. data/lib/mocha/parameter_matchers/object.rb +9 -0
  30. data/lib/mocha/parameter_matchers/optionally.rb +33 -0
  31. data/lib/mocha/parameter_matchers/regexp_matches.rb +5 -2
  32. data/lib/mocha/parameters_matcher.rb +37 -0
  33. data/lib/mocha/pretty_parameters.rb +1 -1
  34. data/lib/mocha/return_values.rb +7 -4
  35. data/lib/mocha/sequence.rb +42 -0
  36. data/lib/mocha/yield_parameters.rb +3 -3
  37. data/test/acceptance/expected_invocation_count_acceptance_test.rb +8 -8
  38. data/test/acceptance/mock_with_initializer_block_acceptance_test.rb +44 -0
  39. data/test/acceptance/optional_parameters_acceptance_test.rb +63 -0
  40. data/test/acceptance/parameter_matcher_acceptance_test.rb +38 -2
  41. data/test/acceptance/partial_mocks_acceptance_test.rb +40 -0
  42. data/test/acceptance/sequence_acceptance_test.rb +179 -0
  43. data/test/integration/mocha_test_result_integration_test.rb +3 -3
  44. data/test/integration/stubba_integration_test.rb +2 -2
  45. data/test/integration/stubba_test_result_integration_test.rb +2 -2
  46. data/test/test_runner.rb +2 -2
  47. data/test/unit/any_instance_method_test.rb +2 -0
  48. data/test/unit/auto_verify_test.rb +10 -3
  49. data/test/unit/central_test.rb +1 -1
  50. data/test/unit/class_method_test.rb +4 -0
  51. data/test/unit/expectation_error_test.rb +24 -0
  52. data/test/unit/expectation_list_test.rb +6 -0
  53. data/test/unit/expectation_test.rb +111 -27
  54. data/test/unit/method_matcher_test.rb +23 -0
  55. data/test/unit/missing_expectation_test.rb +24 -27
  56. data/test/unit/mock_test.rb +29 -22
  57. data/test/unit/object_inspect_test.rb +4 -2
  58. data/test/unit/parameter_matchers/all_of_test.rb +2 -2
  59. data/test/unit/parameter_matchers/any_of_test.rb +2 -2
  60. data/test/unit/parameter_matchers/anything_test.rb +2 -2
  61. data/test/unit/parameter_matchers/has_entries_test.rb +30 -0
  62. data/test/unit/parameter_matchers/has_entry_test.rb +20 -5
  63. data/test/unit/parameter_matchers/has_key_test.rb +2 -2
  64. data/test/unit/parameter_matchers/has_value_test.rb +2 -2
  65. data/test/unit/parameter_matchers/includes_test.rb +2 -2
  66. data/test/unit/parameter_matchers/instance_of_test.rb +2 -2
  67. data/test/unit/parameter_matchers/is_a_test.rb +25 -0
  68. data/test/unit/parameter_matchers/kind_of_test.rb +3 -3
  69. data/test/unit/parameter_matchers/not_test.rb +26 -0
  70. data/test/unit/parameter_matchers/regexp_matches_test.rb +2 -2
  71. data/test/unit/parameter_matchers/stub_matcher.rb +2 -1
  72. data/test/unit/parameters_matcher_test.rb +121 -0
  73. data/test/unit/sequence_test.rb +104 -0
  74. metadata +35 -6
  75. data/test/unit/pretty_parameters_test.rb +0 -32
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/testtask'
5
5
  require 'rake/contrib/sshpublisher'
6
6
 
7
7
  module Mocha
8
- VERSION = "0.5.5"
8
+ VERSION = "0.5.6"
9
9
  end
10
10
 
11
11
  desc "Run all tests"
@@ -96,6 +96,7 @@ specification = Gem::Specification.new do |s|
96
96
  s.name = "mocha"
97
97
  s.summary = "Mocking and stubbing library"
98
98
  s.version = Mocha::VERSION
99
+ s.platform = Gem::Platform::RUBY
99
100
  s.author = 'James Mead'
100
101
  s.description = <<-EOF
101
102
  Mocking and stubbing library with JMock/SchMock syntax, which allows mocking and stubbing of methods on real (non-mock) classes.
@@ -109,6 +110,7 @@ specification = Gem::Specification.new do |s|
109
110
  s.rdoc_options << '--title' << 'Mocha' << '--main' << 'README' << '--line-numbers'
110
111
 
111
112
  s.autorequire = 'mocha'
113
+ s.add_dependency('rake')
112
114
  s.files = FileList['{lib,test,examples}/**/*.rb', '[A-Z]*'].exclude('TODO').to_a
113
115
  end
114
116
 
@@ -1,36 +1,44 @@
1
- # Mocking a class method
2
-
3
- product = Product.new
4
- Product.expects(:find).with(1).returns(product)
5
- assert_equal product, Product.find(1)
6
-
7
- # Mocking an instance method on a real object
8
-
9
- product = Product.new
10
- product.expects(:save).returns(true)
11
- assert product.save
12
-
13
- # Stubbing instance methods on real object
14
-
15
- prices = [stub(:pence => 1000), stub(:pence => 2000)]
16
- product = Product.new
17
- product.stubs(:prices).returns(prices)
18
- assert_equal [1000, 2000], product.prices.collect {|p| p.pence}
19
-
20
- # Stubbing an instance method on all instances of a class
21
-
22
- Product.any_instance.stubs(:name).returns('stubbed_name')
23
- product = Product.new
24
- assert_equal 'stubbed_name', product.name
25
-
26
- # Traditional mocking
27
-
28
- object = mock()
29
- object.expects(:expected_method).with(:p1, :p2).returns(:result)
30
- assert_equal :result, object.expected_method(:p1, :p2)
31
-
32
- # Shortcuts
33
-
34
- object = stub(:method1 => :result1, :method2 => :result2)
35
- assert_equal :result1, object.method1
36
- assert_equal :result2, object.method2
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'mocha'
4
+
5
+ class MiscExampleTest < Test::Unit::TestCase
6
+
7
+ def test_mocking_a_class_method
8
+ product = Product.new
9
+ Product.expects(:find).with(1).returns(product)
10
+ assert_equal product, Product.find(1)
11
+ end
12
+
13
+ def test_mocking_an_instance_method_on_a_real_object
14
+ product = Product.new
15
+ product.expects(:save).returns(true)
16
+ assert product.save
17
+ end
18
+
19
+ def test_stubbing_instance_methods_on_real_objects
20
+ prices = [stub(:pence => 1000), stub(:pence => 2000)]
21
+ product = Product.new
22
+ product.stubs(:prices).returns(prices)
23
+ assert_equal [1000, 2000], product.prices.collect {|p| p.pence}
24
+ end
25
+
26
+ def test_stubbing_an_instance_method_on_all_instances_of_a_class
27
+ Product.any_instance.stubs(:name).returns('stubbed_name')
28
+ product = Product.new
29
+ assert_equal 'stubbed_name', product.name
30
+ end
31
+
32
+ def test_traditional_mocking
33
+ object = mock()
34
+ object.expects(:expected_method).with(:p1, :p2).returns(:result)
35
+ assert_equal :result, object.expected_method(:p1, :p2)
36
+ end
37
+
38
+ def test_shortcuts
39
+ object = stub(:method1 => :result1, :method2 => :result2)
40
+ assert_equal :result1, object.method1
41
+ assert_equal :result2, object.method2
42
+ end
43
+
44
+ end
@@ -21,7 +21,7 @@ class Order
21
21
  end
22
22
 
23
23
  def number_shipped_since(date)
24
- find_all.select { |order| order.shipped_on > date }.size
24
+ find_all.select { |order| order.shipped_on > date }.length
25
25
  end
26
26
 
27
27
  def unshipped_value
@@ -1,4 +1,5 @@
1
1
  require 'mocha/mock'
2
+ require 'mocha/sequence'
2
3
 
3
4
  module Mocha # :nodoc:
4
5
 
@@ -17,9 +18,9 @@ module Mocha # :nodoc:
17
18
  @mocks = nil
18
19
  end
19
20
 
20
- # :call-seq: mock(name) -> mock object
21
- # mock(expected_methods = {}) -> mock object
22
- # mock(name, expected_methods = {}) -> mock object
21
+ # :call-seq: mock(name, &block) -> mock object
22
+ # mock(expected_methods = {}, &block) -> mock object
23
+ # mock(name, expected_methods = {}, &block) -> mock object
23
24
  #
24
25
  # Creates a mock object.
25
26
  #
@@ -27,6 +28,8 @@ module Mocha # :nodoc:
27
28
  #
28
29
  # +expected_methods+ is a +Hash+ with expected method name symbols as keys and corresponding return values as values.
29
30
  #
31
+ # +block+ is a block to be evaluated against the mock object instance, giving an alernative way to set up expectations & stubs.
32
+ #
30
33
  # Note that (contrary to expectations set up by #stub) these expectations <b>must</b> be fulfilled during the test.
31
34
  # def test_product
32
35
  # product = mock('ipod_product', :manufacturer => 'ipod', :price => 100)
@@ -34,17 +37,18 @@ module Mocha # :nodoc:
34
37
  # assert_equal 100, product.price
35
38
  # # an error will be raised unless both Product#manufacturer and Product#price have been called
36
39
  # end
37
- def mock(*args)
38
- name, expectations = name_and_expectations_from_args(args)
39
- mock = Mock.new(false, name)
40
+ def mock(*arguments, &block)
41
+ name = arguments.shift if arguments.first.is_a?(String)
42
+ expectations = arguments.shift || {}
43
+ mock = Mock.new(name, &block)
40
44
  mock.expects(expectations)
41
45
  mocks << mock
42
46
  mock
43
47
  end
44
48
 
45
- # :call-seq: stub(name) -> mock object
46
- # stub(stubbed_methods = {}) -> mock object
47
- # stub(name, stubbed_methods = {}) -> mock object
49
+ # :call-seq: stub(name, &block) -> mock object
50
+ # stub(stubbed_methods = {}, &block) -> mock object
51
+ # stub(name, stubbed_methods = {}, &block) -> mock object
48
52
  #
49
53
  # Creates a mock object.
50
54
  #
@@ -52,6 +56,8 @@ module Mocha # :nodoc:
52
56
  #
53
57
  # +stubbed_methods+ is a +Hash+ with stubbed method name symbols as keys and corresponding return values as values.
54
58
  #
59
+ # +block+ is a block to be evaluated against the mock object instance, giving an alernative way to set up expectations & stubs.
60
+ #
55
61
  # Note that (contrary to expectations set up by #mock) these expectations <b>need not</b> be fulfilled during the test.
56
62
  # def test_product
57
63
  # product = stub('ipod_product', :manufacturer => 'ipod', :price => 100)
@@ -59,22 +65,25 @@ module Mocha # :nodoc:
59
65
  # assert_equal 100, product.price
60
66
  # # an error will not be raised even if Product#manufacturer and Product#price have not been called
61
67
  # end
62
- def stub(*args)
63
- name, expectations = name_and_expectations_from_args(args)
64
- mock = Mock.new(false, name)
65
- mock.stubs(expectations)
66
- mocks << mock
67
- mock
68
+ def stub(*arguments, &block)
69
+ name = arguments.shift if arguments.first.is_a?(String)
70
+ expectations = arguments.shift || {}
71
+ stub = Mock.new(name, &block)
72
+ stub.stubs(expectations)
73
+ mocks << stub
74
+ stub
68
75
  end
69
76
 
70
- # :call-seq: stub_everything(name) -> mock object
71
- # stub_everything(stubbed_methods = {}) -> mock object
72
- # stub_everything(name, stubbed_methods = {}) -> mock object
77
+ # :call-seq: stub_everything(name, &block) -> mock object
78
+ # stub_everything(stubbed_methods = {}, &block) -> mock object
79
+ # stub_everything(name, stubbed_methods = {}, &block) -> mock object
73
80
  #
74
81
  # Creates a mock object that accepts calls to any method.
75
82
  #
76
83
  # By default it will return +nil+ for any method call.
77
84
  #
85
+ # +block+ is a block to be evaluated against the mock object instance, giving an alernative way to set up expectations & stubs.
86
+ #
78
87
  # +name+ and +stubbed_methods+ work in the same way as for #stub.
79
88
  # def test_product
80
89
  # product = stub_everything('ipod_product', :price => 100)
@@ -82,12 +91,14 @@ module Mocha # :nodoc:
82
91
  # assert_nil product.any_old_method
83
92
  # assert_equal 100, product.price
84
93
  # end
85
- def stub_everything(*args)
86
- name, expectations = name_and_expectations_from_args(args)
87
- mock = Mock.new(true, name)
88
- mock.stubs(expectations)
89
- mocks << mock
90
- mock
94
+ def stub_everything(*arguments, &block)
95
+ name = arguments.shift if arguments.first.is_a?(String)
96
+ expectations = arguments.shift || {}
97
+ stub = Mock.new(name, &block)
98
+ stub.stub_everything
99
+ stub.stubs(expectations)
100
+ mocks << stub
101
+ stub
91
102
  end
92
103
 
93
104
  def verify_mocks # :nodoc:
@@ -97,13 +108,9 @@ module Mocha # :nodoc:
97
108
  def teardown_mocks # :nodoc:
98
109
  reset_mocks
99
110
  end
100
-
101
- private
102
-
103
- def name_and_expectations_from_args(args) # :nodoc:
104
- name = args.first.is_a?(String) ? args.delete_at(0) : nil
105
- expectations = args.first || {}
106
- [name, expectations]
111
+
112
+ def sequence(name) # :nodoc:
113
+ Sequence.new(name)
107
114
  end
108
115
 
109
116
  end
@@ -24,7 +24,7 @@ module Mocha
24
24
  end
25
25
 
26
26
  def unstub_all
27
- while stubba_methods.size > 0
27
+ while stubba_methods.length > 0
28
28
  method = stubba_methods.pop
29
29
  method.unstub
30
30
  end
@@ -42,7 +42,11 @@ module Mocha
42
42
  end
43
43
 
44
44
  def hidden_method
45
- method_name = method.to_s.gsub(/\W/) {|s| "_substituted_character_#{s[0]}_" }
45
+ if RUBY_VERSION < '1.9'
46
+ method_name = method.to_s.gsub(/\W/) { |s| "_substituted_character_#{s[0]}_" }
47
+ else
48
+ method_name = method.to_s.gsub(/\W/) { |s| "_substituted_character_#{s.ord}_" }
49
+ end
46
50
  "__stubba__#{method_name}__stubba__"
47
51
  end
48
52
 
@@ -1,5 +1,6 @@
1
1
  require 'mocha/infinite_range'
2
- require 'mocha/pretty_parameters'
2
+ require 'mocha/method_matcher'
3
+ require 'mocha/parameters_matcher'
3
4
  require 'mocha/expectation_error'
4
5
  require 'mocha/return_values'
5
6
  require 'mocha/exception_raiser'
@@ -11,46 +12,6 @@ module Mocha # :nodoc:
11
12
  # Methods on expectations returned from Mock#expects, Mock#stubs, Object#expects and Object#stubs.
12
13
  class Expectation
13
14
 
14
- # :stopdoc:
15
-
16
- class AlwaysEqual
17
- def ==(other)
18
- true
19
- end
20
- end
21
-
22
- attr_reader :method_name, :backtrace
23
-
24
- def initialize(mock, method_name, backtrace = nil)
25
- @mock, @method_name = mock, method_name
26
- @expected_count = 1
27
- @parameters, @parameter_block = AlwaysEqual.new, nil
28
- @invoked_count, @return_values = 0, ReturnValues.new
29
- @backtrace = backtrace || caller
30
- @yield_parameters = YieldParameters.new
31
- @final_expectation = false
32
- end
33
-
34
- def match?(method_name, *arguments)
35
- return false unless @method_name == method_name
36
- if @parameter_block then
37
- return false unless @parameter_block.call(*arguments)
38
- else
39
- return false unless (@parameters == arguments)
40
- end
41
- return true
42
- end
43
-
44
- def invocations_allowed?
45
- if @expected_count.is_a?(Range) then
46
- @invoked_count < @expected_count.last
47
- else
48
- @invoked_count < @expected_count
49
- end
50
- end
51
-
52
- # :startdoc:
53
-
54
15
  # :call-seq: times(range) -> expectation
55
16
  #
56
17
  # Modifies expectation so that the number of calls to the expected method must be within a specific +range+.
@@ -187,9 +148,9 @@ module Mocha # :nodoc:
187
148
  self
188
149
  end
189
150
 
190
- # :call-seq: with(*arguments, &parameter_block) -> expectation
151
+ # :call-seq: with(*expected_parameters, &matching_block) -> expectation
191
152
  #
192
- # Modifies expectation so that the expected method must be called with specified +arguments+.
153
+ # Modifies expectation so that the expected method must be called with +expected_parameters+.
193
154
  # object = mock()
194
155
  # object.expects(:expected_method).with(:param1, :param2)
195
156
  # object.expected_method(:param1, :param2)
@@ -201,7 +162,7 @@ module Mocha # :nodoc:
201
162
  # # => verify fails
202
163
  # May be used with parameter matchers in Mocha::ParameterMatchers.
203
164
  #
204
- # If a +parameter_block+ is given, the block is called with the parameters passed to the expected method.
165
+ # If a +matching_block+ is given, the block is called with the parameters passed to the expected method.
205
166
  # The expectation is matched if the block evaluates to +true+.
206
167
  # object = mock()
207
168
  # object.expects(:expected_method).with() { |value| value % 4 == 0 }
@@ -212,9 +173,8 @@ module Mocha # :nodoc:
212
173
  # object.expects(:expected_method).with() { |value| value % 4 == 0 }
213
174
  # object.expected_method(17)
214
175
  # # => verify fails
215
- def with(*arguments, &parameter_block)
216
- @parameters, @parameter_block = arguments, parameter_block
217
- class << @parameters; def to_s; join(', '); end; end
176
+ def with(*expected_parameters, &matching_block)
177
+ @parameters_matcher = ParametersMatcher.new(expected_parameters, &matching_block)
218
178
  self
219
179
  end
220
180
 
@@ -263,7 +223,7 @@ module Mocha # :nodoc:
263
223
  end
264
224
 
265
225
  # :call-seq: returns(value) -> expectation
266
- # :call-seq: returns(*values) -> expectation
226
+ # returns(*values) -> expectation
267
227
  #
268
228
  # Modifies expectation so that when the expected method is called, it returns the specified +value+.
269
229
  # object = mock()
@@ -339,6 +299,56 @@ module Mocha # :nodoc:
339
299
 
340
300
  # :stopdoc:
341
301
 
302
+ def in_sequence(*sequences)
303
+ sequences.each { |sequence| sequence.constrain_as_next_in_sequence(self) }
304
+ self
305
+ end
306
+
307
+ attr_reader :backtrace
308
+
309
+ def initialize(mock, expected_method_name, backtrace = nil)
310
+ @mock = mock
311
+ @method_matcher = MethodMatcher.new(expected_method_name)
312
+ @parameters_matcher = ParametersMatcher.new
313
+ @ordering_constraints = []
314
+ @expected_count, @invoked_count = 1, 0
315
+ @return_values = ReturnValues.new
316
+ @yield_parameters = YieldParameters.new
317
+ @backtrace = backtrace || caller
318
+ end
319
+
320
+ def add_ordering_constraint(ordering_constraint)
321
+ @ordering_constraints << ordering_constraint
322
+ end
323
+
324
+ def in_correct_order?
325
+ @ordering_constraints.all? { |ordering_constraint| ordering_constraint.allows_invocation_now? }
326
+ end
327
+
328
+ def matches_method?(method_name)
329
+ @method_matcher.match?(method_name)
330
+ end
331
+
332
+ def match?(actual_method_name, *actual_parameters)
333
+ @method_matcher.match?(actual_method_name) && @parameters_matcher.match?(actual_parameters) && in_correct_order?
334
+ end
335
+
336
+ def invocations_allowed?
337
+ if @expected_count.is_a?(Range) then
338
+ @invoked_count < @expected_count.last
339
+ else
340
+ @invoked_count < @expected_count
341
+ end
342
+ end
343
+
344
+ def satisfied?
345
+ if @expected_count.is_a?(Range) then
346
+ @invoked_count >= @expected_count.first
347
+ else
348
+ @invoked_count >= @expected_count
349
+ end
350
+ end
351
+
342
352
  def invoke
343
353
  @invoked_count += 1
344
354
  if block_given? then
@@ -352,27 +362,19 @@ module Mocha # :nodoc:
352
362
  def verify
353
363
  yield(self) if block_given?
354
364
  unless (@expected_count === @invoked_count) then
355
- error = ExpectationError.new(error_message(@expected_count, @invoked_count))
356
- error.set_backtrace(filtered_backtrace)
365
+ error = ExpectationError.new(error_message(@expected_count, @invoked_count), backtrace)
357
366
  raise error
358
367
  end
359
368
  end
360
369
 
361
- def mocha_lib_directory
362
- File.expand_path(File.join(File.dirname(__FILE__), "..")) + File::SEPARATOR
363
- end
364
-
365
- def filtered_backtrace
366
- backtrace.reject { |location| Regexp.new(mocha_lib_directory).match(File.expand_path(location)) }
367
- end
368
-
369
370
  def method_signature
370
- return "#{method_name}" if @parameters.__is_a__(AlwaysEqual)
371
- "#{@method_name}(#{PrettyParameters.new(@parameters).pretty})"
371
+ signature = "#{@mock.mocha_inspect}.#{@method_matcher.mocha_inspect}#{@parameters_matcher.mocha_inspect}"
372
+ signature << "; #{@ordering_constraints.map { |oc| oc.mocha_inspect }.join("; ")}" unless @ordering_constraints.empty?
373
+ signature
372
374
  end
373
375
 
374
376
  def error_message(expected_count, actual_count)
375
- "#{@mock.mocha_inspect}.#{method_signature} - expected calls: #{expected_count.mocha_inspect}, actual calls: #{actual_count}"
377
+ "#{method_signature} - expected calls: #{expected_count.mocha_inspect}, actual calls: #{actual_count}"
376
378
  end
377
379
 
378
380
  # :startdoc: