mocha 0.5.5 → 0.5.6

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.
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: