File: api.rb

package info (click to toggle)
ruby-mocha 2.4.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,540 kB
  • sloc: ruby: 11,899; javascript: 477; makefile: 14
file content (211 lines) | stat: -rw-r--r-- 9,411 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
require 'mocha/ruby_version'
require 'mocha/parameter_matchers'
require 'mocha/hooks'
require 'mocha/mockery'
require 'mocha/sequence'
require 'mocha/object_methods'
require 'mocha/class_methods'

module Mocha
  # Methods added to +Test::Unit::TestCase+, +Minitest::Unit::TestCase+ or equivalent.
  # The mock creation methods are {#mock}, {#stub} and {#stub_everything}, all of which return a #{Mock}
  # which can be further modified by {Mock#responds_like} and {Mock#responds_like_instance_of} methods,
  # both of which return a {Mock}, too, and can therefore, be chained to the original creation methods.
  #
  # {Mock#responds_like} and {Mock#responds_like_instance_of} force the mock to indicate what it is
  # supposed to be mocking, thus making it a safer verifying mock. They check that the underlying +responder+
  # will actually respond to the methods being stubbed, throwing a +NoMethodError+ upon invocation otherwise.
  #
  # @example Verifying mock using {Mock#responds_like_instance_of}
  #   class Sheep
  #     def initialize
  #       raise "some awkward code we don't want to call"
  #     end
  #     def chew(grass); end
  #   end
  #
  #   sheep = mock('sheep').responds_like_instance_of(Sheep)
  #   sheep.expects(:chew)
  #   sheep.expects(:foo)
  #   sheep.respond_to?(:chew) # => true
  #   sheep.respond_to?(:foo) # => false
  #   sheep.chew
  #   sheep.foo # => raises NoMethodError exception
  module API
    include ParameterMatchers
    include Hooks

    # @private
    def self.included(_mod)
      Object.send(:include, Mocha::ObjectMethods)
      Class.send(:include, Mocha::ClassMethods)
    end

    # @private
    def self.extended(mod)
      included(mod)
    end

    # Builds a new mock object
    #
    # @return [Mock] a new mock object
    #
    # @overload def mock(name)
    #   @param [String, Symbol] name identifies mock object in error messages.
    # @overload def mock(expected_methods_vs_return_values = {})
    #   @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {Mock#expects} were called multiple times.
    # @overload def mock(name, expected_methods_vs_return_values = {})
    #   @param [String, Symbol] name identifies mock object in error messages.
    #   @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {Mock#expects} were called multiple times.
    #
    # @example Using expected_methods_vs_return_values Hash to setup expectations.
    #   def test_motor_starts_and_stops
    #     motor = mock('motor', start: true, stop: true)
    #     assert motor.start
    #     assert motor.stop
    #     # an error will be raised unless both Motor#start and Motor#stop have been called
    #   end
    #
    def mock(*arguments)
      name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
      expectations = arguments.shift || {}
      mock = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
      mock.expects(expectations)
      mock
    end

    # Builds a new mock object
    #
    # @return [Mock] a new mock object
    #
    # @overload def stub(name)
    #   @param [String, Symbol] name identifies mock object in error messages.
    # @overload def stub(stubbed_methods_vs_return_values = {})
    #   @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
    # @overload def stub(name, stubbed_methods_vs_return_values = {})
    #   @param [String, Symbol] name identifies mock object in error messages.
    #   @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
    #
    # @example Using stubbed_methods_vs_return_values Hash to setup stubbed methods.
    #   def test_motor_starts_and_stops
    #     motor = stub('motor', start: true, stop: true)
    #     assert motor.start
    #     assert motor.stop
    #     # an error will not be raised even if either Motor#start or Motor#stop has not been called
    #   end
    def stub(*arguments)
      name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
      expectations = arguments.shift || {}
      stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
      stub.stubs(expectations)
      stub
    end

    # Builds a mock object that accepts calls to any method. By default it will return +nil+ for any method call.
    #
    # @return [Mock] a new mock object
    #
    # @overload def stub_everything(name)
    #   @param [String, Symbol] name identifies mock object in error messages.
    # @overload def stub_everything(stubbed_methods_vs_return_values = {})
    #   @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
    # @overload def stub_everything(name, stubbed_methods_vs_return_values = {})
    #   @param [String, Symbol] name identifies mock object in error messages.
    #   @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
    #
    # @example Ignore invocations of irrelevant methods.
    #   def test_motor_stops
    #     motor = stub_everything('motor', stop: true)
    #     assert_nil motor.irrelevant_method_1 # => no error raised
    #     assert_nil motor.irrelevant_method_2 # => no error raised
    #     assert motor.stop
    #   end
    def stub_everything(*arguments)
      name = arguments.shift if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol)
      expectations = arguments.shift || {}
      stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock
      stub.stub_everything
      stub.stubs(expectations)
      stub
    end

    # Builds a new sequence which can be used to constrain the order in which expectations can occur.
    #
    # Specify that an expected invocation must occur within a named {Sequence} by calling {Expectation#in_sequence}
    # on each expectation or by passing a block within which all expectations should be constrained by the {Sequence}.
    #
    # @param [String] name name of sequence
    # @yield optional block within which expectations should be constrained by the sequence
    # @return [Sequence] a new sequence
    #
    # @see Expectation#in_sequence
    #
    # @example Ensure methods on egg are invoked in correct order.
    #   breakfast = sequence('breakfast')
    #
    #   egg = mock('egg')
    #   egg.expects(:crack).in_sequence(breakfast)
    #   egg.expects(:fry).in_sequence(breakfast)
    #   egg.expects(:eat).in_sequence(breakfast)
    #
    # @example Ensure methods across multiple objects are invoked in correct order.
    #   sequence = sequence(:task_order)
    #
    #   task_one = mock("task_one")
    #   task_two = mock("task_two")
    #
    #   task_one.expects(:execute).in_sequence(sequence)
    #   task_two.expects(:execute).in_sequence(sequence)
    #
    #   task_one.execute
    #   task_two.execute
    #
    # @example Ensure methods on egg are invoked in the correct order using a block.
    #   egg = mock('egg')
    #   sequence('breakfast') do
    #     egg.expects(:crack)
    #     egg.expects(:fry)
    #     egg.expects(:eat)
    #   end
    def sequence(name)
      Sequence.new(name).tap do |seq|
        Mockery.instance.sequences.push(seq)
        begin
          yield if block_given?
        ensure
          Mockery.instance.sequences.pop
        end
      end
    end

    # Builds a new state machine which can be used to constrain the order in which expectations can occur.
    #
    # Specify the initial state of the state machine by using {StateMachine#starts_as}.
    #
    # Specify that an expected invocation should change the state of the state machine by using {Expectation#then}.
    #
    # Specify that an expected invocation should be constrained to occur within a particular +state+ by using {Expectation#when}.
    #
    # A test can contain multiple state machines.
    #
    # @param [String] name name of state machine
    # @return [StateMachine] a new state machine
    #
    # @see Expectation#then
    # @see Expectation#when
    # @see StateMachine
    # @example Constrain expected invocations to occur in particular states.
    #   power = states('power').starts_as('off')
    #
    #   radio = mock('radio')
    #   radio.expects(:switch_on).then(power.is('on'))
    #   radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
    #   radio.expects(:adjust_volume).with(+5).when(power.is('on'))
    #   radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
    #   radio.expects(:adjust_volume).with(-5).when(power.is('on'))
    #   radio.expects(:switch_off).then(power.is('off'))
    def states(name)
      Mockery.instance.new_state_machine(name)
    end
  end
end