flexmock 1.3.2 → 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
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