flexmock 1.3.2 → 1.3.3

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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile.lock +9 -9
  3. data/Rakefile +3 -1
  4. data/lib/flexmock.rb +1 -1
  5. data/lib/flexmock/argument_matchers.rb +1 -1
  6. data/lib/flexmock/argument_types.rb +1 -2
  7. data/lib/flexmock/base.rb +2 -1
  8. data/lib/flexmock/call_record.rb +30 -0
  9. data/lib/flexmock/call_validator.rb +68 -0
  10. data/lib/flexmock/composite_expectation.rb +56 -0
  11. data/lib/flexmock/core.rb +30 -64
  12. data/lib/flexmock/core_class_methods.rb +1 -1
  13. data/lib/flexmock/default_framework_adapter.rb +1 -1
  14. data/lib/flexmock/deprecated_methods.rb +8 -3
  15. data/lib/flexmock/errors.rb +1 -1
  16. data/lib/flexmock/expectation.rb +7 -84
  17. data/lib/flexmock/expectation_builder.rb +133 -0
  18. data/lib/flexmock/expectation_director.rb +6 -4
  19. data/lib/flexmock/expectation_recorder.rb +42 -0
  20. data/lib/flexmock/extensions/active_record_model.rb +109 -0
  21. data/lib/flexmock/mock_builder.rb +139 -0
  22. data/lib/flexmock/mock_container.rb +13 -214
  23. data/lib/flexmock/noop.rb +1 -1
  24. data/lib/flexmock/ordering.rb +1 -2
  25. data/lib/flexmock/partial_mock.rb +95 -57
  26. data/lib/flexmock/rails.rb +1 -1
  27. data/lib/flexmock/recorder.rb +4 -3
  28. data/lib/flexmock/rspec.rb +1 -1
  29. data/lib/flexmock/rspec_spy_matcher.rb +4 -0
  30. data/lib/flexmock/spy_describers.rb +22 -5
  31. data/lib/flexmock/test_unit.rb +2 -40
  32. data/lib/flexmock/test_unit_integration.rb +4 -4
  33. data/lib/flexmock/test_unit_testcase_extensions.rb +49 -0
  34. data/lib/flexmock/undefined.rb +1 -1
  35. data/lib/flexmock/validators.rb +18 -12
  36. data/lib/flexmock/version.rb +1 -1
  37. data/test/based_partials_test.rb +1 -1
  38. data/test/container_methods_test.rb +1 -1
  39. data/test/default_framework_adapter_test.rb +1 -1
  40. data/test/demeter_mocking_test.rb +52 -2
  41. data/test/deprecated_methods_test.rb +1 -1
  42. data/test/examples_from_readme_test.rb +1 -1
  43. data/test/expectation_description_test.rb +1 -1
  44. data/test/extended_should_receive_test.rb +1 -1
  45. data/test/mock_builder_test.rb +68 -0
  46. data/test/naming_test.rb +1 -1
  47. data/test/new_instances_test.rb +1 -1
  48. data/test/partial_mock_test.rb +23 -6
  49. data/test/record_mode_test.rb +1 -1
  50. data/test/rspec_integration/integration_spec.rb +1 -1
  51. data/test/samples_test.rb +1 -2
  52. data/test/should_ignore_missing_test.rb +1 -1
  53. data/test/should_receive_test.rb +1 -1
  54. data/test/test_setup.rb +17 -0
  55. data/test/test_unit_integration/auto_test_unit_test.rb +1 -1
  56. data/test/tu_integration_test.rb +1 -1
  57. data/test/undefined_test.rb +1 -1
  58. metadata +52 -52
  59. data/TAGS +0 -1056
  60. data/lib/flexmock/composite.rb +0 -10
  61. data/lib/flexmock/rails/view_mocking.rb +0 -144
  62. data/test/rails_view_stub_test.rb +0 -145
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  #---
4
- # Copyright 2003-2012 by Jim Weirich (jim.weirich@gmail.com).
4
+ # Copyright 2003-2013 by Jim Weirich (jim.weirich@gmail.com).
5
5
  # All rights reserved.
6
6
  #
7
7
  # Permission is granted for use, copying, modification, distribution,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  #---
4
- # Copyright 2003-2012 by Jim Weirich (jim.weirich@gmail.com).
4
+ # Copyright 2003-2013 by Jim Weirich (jim.weirich@gmail.com).
5
5
  # All rights reserved.
6
6
 
7
7
  # Permission is granted for use, copying, modification, distribution,
@@ -11,10 +11,10 @@
11
11
 
12
12
  require 'flexmock/noop'
13
13
  require 'flexmock/argument_matching'
14
+ require 'flexmock/expectation_recorder'
14
15
 
15
16
  class FlexMock
16
17
 
17
- ####################################################################
18
18
  # An Expectation is returned from each +should_receive+ message sent
19
19
  # to mock object. Each expectation records how a message matching
20
20
  # the message name (argument to +should_receive+) and the argument
@@ -386,7 +386,11 @@ class FlexMock
386
386
 
387
387
  # Helper method for defining ordered expectations.
388
388
  def define_ordered(group_name, ordering)
389
- fail UsageError, "Mock #{@mock.flexmock_name} is not in a container and cannot be globally ordered." if ordering.nil?
389
+ if ordering.nil?
390
+ fail UsageError,
391
+ "Mock #{@mock.flexmock_name} " +
392
+ "is not in a container and cannot be globally ordered."
393
+ end
390
394
  if group_name.nil?
391
395
  result = ordering.flexmock_allocate_order
392
396
  elsif (num = ordering.flexmock_groups[group_name])
@@ -419,85 +423,4 @@ class FlexMock
419
423
 
420
424
  end
421
425
 
422
- ##########################################################################
423
- # A composite expectation allows several expectations to be grouped into a
424
- # single composite and then apply the same constraints to all expectations
425
- # in the group.
426
- class CompositeExpectation
427
-
428
- # Initialize the composite expectation.
429
- def initialize
430
- @expectations = []
431
- end
432
-
433
- # Add an expectation to the composite.
434
- def add(expectation)
435
- @expectations << expectation
436
- end
437
-
438
- # Apply the constraint method to all expectations in the composite.
439
- def method_missing(sym, *args, &block)
440
- @expectations.each do |expectation|
441
- expectation.send(sym, *args, &block)
442
- end
443
- self
444
- end
445
-
446
- # The following methods return a value, so we make an arbitrary choice
447
- # and return the value for the first expectation in the composite.
448
-
449
- # Return the order number of the first expectation in the list.
450
- def order_number
451
- @expectations.first.order_number
452
- end
453
-
454
- # Return the associated mock object.
455
- def mock
456
- @expectations.first.mock
457
- end
458
-
459
- # Start a new method expectation. The following constraints will be
460
- # applied to the new expectation.
461
- def should_receive(*args, &block)
462
- @expectations.first.mock.flexmock_define_expectation(caller.first, *args, &block)
463
- end
464
-
465
- # Return a string representations
466
- def to_s
467
- if @expectations.size > 1
468
- "[" + @expectations.collect { |e| e.to_s }.join(', ') + "]"
469
- else
470
- @expectations.first.to_s
471
- end
472
- end
473
- end
474
-
475
- ##########################################################################
476
- # An expectation recorder records any expectations received and plays them
477
- # back on demand. This is used to collect the expectations in the blockless
478
- # version of the new_instances call.
479
- #
480
- class ExpectationRecorder
481
-
482
- # Initialize the recorder.
483
- def initialize
484
- @expectations = []
485
- end
486
-
487
- # Save any incoming messages to be played back later.
488
- def method_missing(sym, *args, &block)
489
- @expectations << [sym, args, block]
490
- self
491
- end
492
-
493
- # Apply the recorded messages to the given object in a chaining fashion
494
- # (i.e. the result of the previous call is used as the target of the next
495
- # call).
496
- def apply(mock)
497
- obj = mock
498
- @expectations.each do |sym, args, block|
499
- obj = obj.send(sym, *args, &block)
500
- end
501
- end
502
- end
503
426
  end
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003-2013 by Jim Weirich (jim.weirich@gmail.com).
5
+ # All rights reserved.
6
+
7
+ # Permission is granted for use, copying, modification, distribution,
8
+ # and distribution of modified versions of this work as long as the
9
+ # above copyright notice is included.
10
+ #+++
11
+
12
+ require 'flexmock/composite_expectation'
13
+
14
+ class FlexMock
15
+
16
+ class ExpectationBuilder
17
+ # :call-seq:
18
+ # parse_should_args(args) { |symbol| ... }
19
+ #
20
+ # This method provides common handling for the various should_receive
21
+ # argument lists. It sorts out the differences between symbols, arrays and
22
+ # hashes, and identifies the method names specified by each. As each
23
+ # method name is identified, create a mock expectation for it using the
24
+ # supplied block.
25
+ def parse_should_args(mock, args, &block) # :nodoc:
26
+ result = CompositeExpectation.new
27
+ args.each do |arg|
28
+ case arg
29
+ when Hash
30
+ arg.each do |k,v|
31
+ exp = create_expectation(mock, k, &block).and_return(v)
32
+ result.add(exp)
33
+ end
34
+ when Symbol, String
35
+ result.add(create_expectation(mock, arg, &block))
36
+ end
37
+ end
38
+ result
39
+ end
40
+
41
+ # Create an expectation for the name on this mock. For simple
42
+ # mocks, this is done by calling the provided block parameter and
43
+ # letting the calling site handle the creation of the expectation
44
+ # (which differs between full mocks and partial mocks).
45
+ #
46
+ # If the name_chain contains demeter mocking chains, then the
47
+ # process is more complex. A series of mocks are created, each
48
+ # component of the chain returning the next mock until the
49
+ # expectation for the last component is returned.
50
+ def create_expectation(mock, name_chain, &block)
51
+ names = name_chain.to_s.split('.').map { |n| n.to_sym }
52
+ check_method_names(names)
53
+ if names.size == 1
54
+ block.call(names.first)
55
+ elsif names.size > 1
56
+ create_demeter_chain(mock, names)
57
+ else
58
+ fail "Empty list of names"
59
+ end
60
+ end
61
+
62
+ # Build the chain of mocks for demeter style mocking.
63
+ #
64
+ # This method builds a chain of mocks to support demeter style
65
+ # mocking. Given a mock chain of "first.second.third.last", we
66
+ # must build a chain of mock methods that return the next mock in
67
+ # the chain. The expectation for the last method of the chain is
68
+ # returned as the result of the method.
69
+ #
70
+ # Things to consider:
71
+ #
72
+ # * The expectations for all methods but the last in the chain
73
+ # will be setup to expect no parameters and to return the next
74
+ # mock in the chain.
75
+ #
76
+ # * It could very well be the case that several demeter chains
77
+ # will be defined on a single mock object, and those chains
78
+ # could share some of the same methods (e.g. "mock.one.two.read"
79
+ # and "mock.one.two.write" both share the methods "one" and
80
+ # "two"). It is important that the shared methods return the
81
+ # same mocks in both chains.
82
+ #
83
+ def create_demeter_chain(mock, names)
84
+ container = mock.flexmock_container
85
+ last_method = names.pop
86
+ names.each do |name|
87
+ exp = mock.flexmock_find_expectation(name)
88
+ if exp
89
+ next_mock = exp._return_value([])
90
+ check_proper_mock(next_mock, name)
91
+ else
92
+ next_mock = container.flexmock("demeter_#{name}")
93
+ mock.should_receive(name).and_return(next_mock)
94
+ end
95
+ mock = next_mock
96
+ end
97
+ mock.should_receive(last_method)
98
+ end
99
+
100
+ # Check that the given mock is a real FlexMock mock.
101
+ def check_proper_mock(mock, method_name)
102
+ unless mock.respond_to?(:should_receive)
103
+ fail FlexMock::UsageError,
104
+ "Conflicting mock declaration for '#{method_name}' in demeter style mock"
105
+ end
106
+ end
107
+
108
+ METHOD_NAME_ALTS = [
109
+ '[A-Za-z_][A-Za-z0-9_]*[=!?]?',
110
+ '\[\]=?',
111
+ '\*\\*',
112
+ '<<',
113
+ '>>',
114
+ '<=>',
115
+ '[<>=!]=',
116
+ '[=!]~',
117
+ '===',
118
+ '[-+]@',
119
+ '[-+\*\/%&^|<>~`!]'
120
+ ].join("|")
121
+ METHOD_NAME_RE = /^(#{METHOD_NAME_ALTS})$/
122
+
123
+ # Check that all the names in the list are valid method names.
124
+ def check_method_names(names)
125
+ names.each do |name|
126
+ fail FlexMock::UsageError, "Ill-formed method name '#{name}'" if
127
+ name.to_s !~ METHOD_NAME_RE
128
+ end
129
+ end
130
+ end
131
+
132
+ EXP_BUILDER = ExpectationBuilder.new
133
+ end
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  #---
4
- # Copyright 2003-2012 by Jim Weirich (jim.weirich@gmail.com).
4
+ # Copyright 2003-2013 by Jim Weirich (jim.weirich@gmail.com).
5
5
  # All rights reserved.
6
6
 
7
7
  # Permission is granted for use, copying, modification, distribution,
@@ -13,7 +13,7 @@ require 'flexmock/noop'
13
13
  require 'flexmock/errors'
14
14
 
15
15
  class FlexMock
16
- ####################################################################
16
+
17
17
  # The expectation director is responsible for routing calls to the
18
18
  # correct expectations for a given argument list.
19
19
  #
@@ -39,8 +39,10 @@ class FlexMock
39
39
  exp = find_expectation(*args)
40
40
  call_record.expectation = exp if call_record
41
41
  FlexMock.check(
42
- "no matching handler found for " + FlexMock.format_call(@sym, args)) { ! exp.nil? }
43
- exp.verify_call(*args)
42
+ "no matching handler found for " +
43
+ FlexMock.format_call(@sym, args)) { ! exp.nil? }
44
+ returned_value = exp.verify_call(*args)
45
+ returned_value
44
46
  end
45
47
 
46
48
  # Append an expectation to this director.
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #---
4
+ # Copyright 2003-2013 by Jim Weirich (jim.weirich@gmail.com).
5
+ # All rights reserved.
6
+
7
+ # Permission is granted for use, copying, modification, distribution,
8
+ # and distribution of modified versions of this work as long as the
9
+ # above copyright notice is included.
10
+ #+++
11
+
12
+ class FlexMock
13
+
14
+ # An expectation recorder records any expectations received and plays them
15
+ # back on demand. This is used to collect the expectations in the blockless
16
+ # version of the new_instances call.
17
+ #
18
+ class ExpectationRecorder
19
+
20
+ # Initialize the recorder.
21
+ def initialize
22
+ @expectations = []
23
+ end
24
+
25
+ # Save any incoming messages to be played back later.
26
+ def method_missing(sym, *args, &block)
27
+ @expectations << [sym, args, block]
28
+ self
29
+ end
30
+
31
+ # Apply the recorded messages to the given object in a chaining fashion
32
+ # (i.e. the result of the previous call is used as the target of the next
33
+ # call).
34
+ def apply(mock)
35
+ obj = mock
36
+ @expectations.each do |sym, args, block|
37
+ obj = obj.send(sym, *args, &block)
38
+ end
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,109 @@
1
+ require 'flexmock/mock_container'
2
+
3
+ class FlexMock
4
+ module Extensions
5
+
6
+ class ActiveRecordModel
7
+ # Handle the argument list.
8
+ #
9
+ # This method is called whenever an unrecognized symbol is
10
+ # detected in the flexmock argument list. If the extension class
11
+ # can handle it, it should return true.
12
+ #
13
+ # Extension data can be stored in the opts.data hash for later use
14
+ # during the create and post_create phase.
15
+ def handle(args, opts)
16
+ return false unless args.first == :model
17
+ args.shift
18
+ opts.data[:model_class] = args.shift
19
+ opts.extended = self
20
+ true
21
+ end
22
+
23
+ # Create the test double.
24
+ #
25
+ # Create the custome test double according to the data from the
26
+ # argument list. The object returned from this method is the
27
+ # object returned from the original flexmock() method call. This
28
+ # the returned object is NOT the actual mock object (which is the
29
+ # case for things like partial proxies), then the opts.mock field
30
+ # should be set to contain the actual mock object.
31
+ def create(container, opts)
32
+ id = next_id
33
+ FlexMock.new("#{opts.data[:model_class]}_#{id}", container)
34
+ end
35
+
36
+ # Do any post-creation setup on the mock object.
37
+ def post_create(opts, location)
38
+ add_model_methods(opts.mock, opts.data[:model_class], location)
39
+ end
40
+
41
+ private
42
+
43
+ # Return the next id for mocked models.
44
+ def next_id
45
+ @id_counter ||= 10000
46
+ @id_counter += 1
47
+ end
48
+
49
+ def current_id
50
+ @id_counter
51
+ end
52
+
53
+ # Automatically add mocks for some common methods in ActiveRecord
54
+ # models.
55
+ def add_model_methods(mock, model_class, location)
56
+ add_model_methods_returning_values(mock, location,
57
+ [:id, current_id ],
58
+ [:to_params, current_id.to_s ],
59
+ [:new_record?, false ],
60
+ [:class, model_class ],
61
+ [:errors, make_mock_model_errors_for(mock, location) ])
62
+
63
+ add_model_methods_with_behavior(mock, location,
64
+ [:is_a?, lambda { |other| other == model_class } ],
65
+ [:instance_of?, lambda { |other| other == model_class } ],
66
+ [:kind_of?, lambda { |other| model_class.ancestors.include?(other) } ])
67
+ end
68
+
69
+ def add_model_methods_returning_values(mock, location, *pairs)
70
+ pairs.each do |method, retval|
71
+ make_default_behavior(mock, location, method, retval)
72
+ end
73
+ end
74
+
75
+ def add_model_methods_with_behavior(mock, location, *pairs)
76
+ pairs.each do |method, block|
77
+ make_default_behavior(mock, location, method, &block)
78
+ end
79
+ end
80
+
81
+ # Create a mock model errors object (with default behavior).
82
+ def make_mock_model_errors_for(mock, location)
83
+ result = mock.flexmock_container.flexmock("errors")
84
+ make_default_behavior(result, location, :count, 0)
85
+ make_default_behavior(result, location, :full_messages, [])
86
+ result
87
+ end
88
+
89
+ # Define default behavior on a mock object.
90
+ #
91
+ # If a block is given, use that to define the behavior. Otherwise
92
+ # return the +retval+ value.
93
+ def make_default_behavior(mock, location, method, retval=nil, &block)
94
+ if block_given?
95
+ mock.flexmock_define_expectation(location, method).
96
+ with(FlexMock.any).
97
+ and_return(&block).
98
+ by_default
99
+ else
100
+ mock.flexmock_define_expectation(location, method).
101
+ and_return(retval).
102
+ by_default
103
+ end
104
+ end
105
+ end
106
+
107
+ FlexMock::CONTAINER_HELPER.add_extension(ActiveRecordModel.new)
108
+ end
109
+ end
@@ -0,0 +1,139 @@
1
+ class FlexMock
2
+
3
+ # This class contains helper methods for mock containers. Since
4
+ # MockContainer is a module that is designed to be mixed into other
5
+ # classes, (particularly testing framework test cases), we don't
6
+ # want to pollute the method namespace of the class that mixes in
7
+ # MockContainer. So we have aggressively moved a number of
8
+ # MockContainer methods out of that class and into
9
+ # MockBuilder to isoloate the names.
10
+ #
11
+ class MockBuilder
12
+ def initialize(container)
13
+ @container = container
14
+ end
15
+
16
+ def define_a_mock(location, *args, &block)
17
+ opts = parse_creation_args(args)
18
+ if opts.safe_mode && ! block_given?
19
+ raise UsageError, "a block is required in safe mode"
20
+ end
21
+
22
+ result = create_double(opts)
23
+ flexmock_mock_setup(opts.mock, opts, location, &block)
24
+ run_post_creation_hooks(opts, location)
25
+ result
26
+ end
27
+
28
+ FlexOpts = Struct.new(
29
+ :name, :defs, :safe_mode, :mock,
30
+ :domain_obj, :base_class,
31
+ :extended, :extended_data
32
+ ) do
33
+ def data
34
+ self.extended_data ||= {}
35
+ end
36
+ end
37
+
38
+ # Parse the list of flexmock() arguments and populate the opts object.
39
+ def parse_creation_args(args)
40
+ opts = FlexOpts.new
41
+ while ! args.empty?
42
+ case args.first
43
+ when Symbol
44
+ unless parse_create_symbol(args, opts)
45
+ opts.name = args.shift.to_s
46
+ end
47
+ when String, Symbol
48
+ opts.name = args.shift.to_s
49
+ when Hash
50
+ opts.defs = args.shift
51
+ when FlexMock
52
+ opts.mock = args.shift
53
+ else
54
+ opts.domain_obj = args.shift
55
+ end
56
+ end
57
+ set_base_class(opts)
58
+ opts
59
+ end
60
+
61
+ # Create the test double based on the args options.
62
+ def create_double(opts)
63
+ if opts.extended
64
+ result = opts.extended.create(container, opts)
65
+ elsif opts.domain_obj
66
+ result = create_partial(opts)
67
+ else
68
+ result = create_mock(opts)
69
+ end
70
+ opts.mock ||= result
71
+ result
72
+ end
73
+
74
+ # Run any post creation hooks specified by an extension.
75
+ def run_post_creation_hooks(opts, location)
76
+ if opts.extended
77
+ opts.extended.post_create(opts, location)
78
+ end
79
+ end
80
+
81
+
82
+ # Setup the test double with its expections and such.
83
+ def flexmock_mock_setup(mock, opts, location)
84
+ mock.flexmock_based_on(opts.base_class) if opts.base_class
85
+ mock.flexmock_define_expectation(location, opts.defs)
86
+ yield(mock) if block_given?
87
+ container.flexmock_remember(mock)
88
+ end
89
+
90
+ attr_reader :container
91
+ private :container
92
+
93
+ private
94
+
95
+ # Set the base class if not defined and partials are based.
96
+ def set_base_class(opts)
97
+ if ! opts.base_class && opts.domain_obj && FlexMock.partials_are_based
98
+ opts.base_class = opts.domain_obj.class
99
+ end
100
+ end
101
+
102
+ # Handle a symbol in the flexmock() args list.
103
+ def parse_create_symbol(args, opts)
104
+ case args.first
105
+ when :base, :safe
106
+ opts.safe_mode = (args.shift == :safe)
107
+ opts.domain_obj = args.shift
108
+ when :on
109
+ args.shift
110
+ opts.base_class = args.shift
111
+ opts.name ||= "#{opts.base_class} Mock"
112
+ else
113
+ CONTAINER_HELPER.extensions.each do |ext|
114
+ handled = ext.handle(args, opts)
115
+ return true if handled
116
+ end
117
+ return false
118
+ end
119
+ true
120
+ end
121
+
122
+ # Create a mock object in the options.
123
+ def create_mock(opts)
124
+ opts.mock ||= FlexMock.new(opts.name || "unknown", container)
125
+ opts.mock
126
+ end
127
+
128
+ # Create a partial mock object in options.
129
+ def create_partial(opts)
130
+ opts.mock = PartialMockProxy.make_proxy_for(
131
+ opts.domain_obj,
132
+ container, opts.name,
133
+ opts.safe_mode)
134
+ opts.domain_obj
135
+ end
136
+
137
+ end
138
+
139
+ end