mockr 0.1

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 (3) hide show
  1. data/lib/mockr.rb +222 -0
  2. data/test/mockr_tests.rb +165 -0
  3. metadata +47 -0
data/lib/mockr.rb ADDED
@@ -0,0 +1,222 @@
1
+ require 'test/unit'
2
+
3
+ module Mockr
4
+
5
+ ################################################################################
6
+
7
+ class Mock
8
+ include ::Test::Unit
9
+
10
+ attr_reader :proxy
11
+
12
+ # Create a new Mock, with an optional initialisation block. If provided,
13
+ # the block will be called with the new instance.
14
+ #
15
+ # Example:
16
+ # Mock.new do |m|
17
+ # m.stubs.some_method
18
+ # end
19
+ def initialize &block
20
+ @expectations = []
21
+ @satisfied_expectations = []
22
+ create_proxy
23
+ block.call(self) if block
24
+ end
25
+
26
+ # Tell the mock to respond to a call, optionally with specific parameters.
27
+ #
28
+ # The call can be called an arbitrary number of times by the client code
29
+ # without affecting the result of #verify.
30
+ #
31
+ # Parameters to the expected call will be used to match the actual parameters
32
+ # passed by client code later. The match (===) method of the expectation
33
+ # parameter is used to determine whether the client's call matched this
34
+ # anticipated call.
35
+ #
36
+ # Examples:
37
+ # mock.stubs.bang!
38
+ # mock.stubs.ping.as { :pong }
39
+ # mock.stubs.hello?(/World/).as { true } # Respond with +true+ if called with a parameter for which <tt>/World/ === param</tt> is true
40
+ def stubs
41
+ CallRecorder.new(method(:stub_call))
42
+ end
43
+
44
+ # Tell the mock to expect a call, optionally with specific parameters.
45
+ # If the call has not been made when #verify is called, #verify will fail
46
+ # with a Test::Unit::AssertionFailed.
47
+ #
48
+ # Parameters to the expected call will be used to match the actual parameters
49
+ # passed by client code later. The match (===) method of the expectation
50
+ # parameter is used to determine whether the client's call matched this anticipated
51
+ # call.
52
+ #
53
+ # Examples:
54
+ # mock.expects.bang!
55
+ # mock.expects.ping.as { :pong }
56
+ # mock.expects.safe?(1..10).as { true } # Expect a call with a parameter for which <tt>(1..10) === param</tt>
57
+ def expects
58
+ CallRecorder.new(method(:expect_call))
59
+ end
60
+
61
+ # Check that the expected calls to this mock were made, and
62
+ # raise a Test::Unit::AssertionFailed exception otherwise. This
63
+ # method will be called automatically if you use the methods provided
64
+ # by TestCaseHelpers
65
+ def verify
66
+ missing_expectations = @expectations - @satisfied_expectations
67
+ if missing_expectations.any?
68
+ raise AssertionFailedError.new("Expected #{missing_expectations[0]} did not happen")
69
+ end
70
+ end
71
+
72
+ # Execute the given block and call #verify afterwards. You are unlikely
73
+ # to use this method, since the methods in TestCaseHelpers render it
74
+ # somewhat redundant.
75
+ def use &block
76
+ block.call(proxy)
77
+ verify
78
+ end
79
+
80
+ private
81
+
82
+ def create_proxy
83
+ @proxy = Object.new
84
+ @stubs = @proxy.instance_eval '@stubs = {}'
85
+ end
86
+
87
+ def stub_for(method_name)
88
+ mock_method = @stubs[method_name]
89
+ unless mock_method
90
+ mock_method = @stubs[method_name]= Response.new
91
+ install_method_in_proxy(method_name)
92
+ end
93
+ mock_method
94
+ end
95
+
96
+ def stub_call(method_name, *argspec)
97
+ stub_for(method_name).add_handler(CallMatcher.new(method_name, argspec))
98
+ end
99
+
100
+ def expect_call(method_name, *argspec)
101
+ handler = CallMatcher.new(method_name, argspec) do |satisfied|
102
+ if @satisfied_expectations.include?(satisfied)
103
+ raise AssertionFailedError.new("Unexpected extra call to #{method_name}")
104
+ end
105
+ @satisfied_expectations << satisfied
106
+ end
107
+ @expectations << stub_for(method_name).add_handler(handler)
108
+ handler
109
+ end
110
+
111
+ def install_method_in_proxy(method_name)
112
+ @proxy.instance_eval(<<-EOF)
113
+ def #{method_name.to_s}(*args, &block)
114
+ args << block if block
115
+ @stubs[:#{method_name}].call(*args)
116
+ end
117
+ EOF
118
+ end
119
+ end
120
+
121
+ ################################################################################
122
+ private
123
+ ################################################################################
124
+
125
+ class CallMatcher # :nodoc:
126
+
127
+ def initialize(method_name, argspec, &listener)
128
+ @method_name = method_name
129
+ @argspec = argspec
130
+ @response = lambda { }
131
+ @listener = listener
132
+ end
133
+
134
+ def ===(args)
135
+ return false if args.size > @argspec.size
136
+ @argspec.zip(args).each do |expected, actual|
137
+ return false unless expected === actual
138
+ end
139
+ true
140
+ end
141
+
142
+ def call
143
+ @listener.call(self) if @listener
144
+ @response.call
145
+ end
146
+
147
+ def to_s
148
+ "call to #{@method_name} with args #{@argspec.inspect}"
149
+ end
150
+
151
+ def as(&block)
152
+ @response = block
153
+ end
154
+ end
155
+
156
+ ################################################################################
157
+
158
+ class Response # :nodoc:
159
+ include ::Test::Unit
160
+
161
+ def initialize
162
+ @handlers = []
163
+ end
164
+
165
+ def add_handler(handler)
166
+ @handlers << handler; handler
167
+ end
168
+
169
+ def call(*args, &block)
170
+ args << block if block
171
+ @handlers.each do |handler|
172
+ return handler.call if handler === args
173
+ end
174
+ raise AssertionFailedError.new("no match for arguments: #{args.inspect}")
175
+ end
176
+ end
177
+
178
+ class CallRecorder # :nodoc:
179
+ def initialize(on_call)
180
+ @on_call = on_call
181
+ end
182
+ public_instance_methods.each do |meth|
183
+ undef_method(meth) unless %w(__id__ __send__ method_missing).include?(meth)
184
+ end
185
+ def method_missing(meth, *args)
186
+ @on_call.call(meth, *args)
187
+ end
188
+ end
189
+
190
+ # Include this module in your Test::Unit::TestCase in order to more conveniently
191
+ # use and verify mocks.
192
+ module TestCaseHelpers
193
+
194
+ # Create a new mock and remember it so that it can be automatically
195
+ # verified when the test case completes
196
+ def new_mock
197
+ @mocks ||= []
198
+ @mocks << (mock = Mock.new)
199
+ mock
200
+ end
201
+
202
+ alias mock new_mock
203
+
204
+ def teardown # :nodoc:
205
+ verify_all_mocks
206
+ end
207
+
208
+ # Verify all mocks that were created using #new_mock.
209
+ #
210
+ # Usually you will not
211
+ # need to call this method yourself -- it is called automatically unless
212
+ # you override #teardown for your own purposes
213
+ def verify_all_mocks
214
+ return unless instance_variables.include?("@mocks")
215
+ @mocks.each do |mock|
216
+ mock.verify
217
+ end
218
+ end
219
+
220
+ end
221
+
222
+ end
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env ruby
2
+ $VERBOSE = true if $0 == __FILE__
3
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
4
+
5
+ require 'test/unit'
6
+ require 'mockr'
7
+
8
+
9
+ module Mockr::Test
10
+
11
+ class MockTest < Test::Unit::TestCase
12
+ include ::Test::Unit
13
+ include Mockr
14
+
15
+ def test_stub_call_with_no_arguments_returns_nil
16
+ mock = Mock.new
17
+ mock.stubs.ping
18
+ assert_nil mock.proxy.ping
19
+ end
20
+
21
+ def test_stub_call_with_no_arguments_will_return_preset_value
22
+ mock = Mock.new
23
+ mock.stubs.ping.as {'pong'}
24
+ assert_equal 'pong', mock.proxy.ping
25
+ end
26
+
27
+ def test_can_construct_mock_with_a_block
28
+ mock = Mock.new do |m|
29
+ m.stubs.ping.as {'pong'}
30
+ end
31
+ assert_equal 'pong', mock.proxy.ping
32
+ end
33
+
34
+ def test_error_raised_if_missing_mock_method_invoked
35
+ proxy = Mock.new.proxy
36
+ assert_raise NoMethodError do proxy.ping end
37
+ end
38
+
39
+ def test_verify_after_calling_no_stubs_does_nothing
40
+ Mock.new.verify
41
+ Mock.new { |m| m.stubs.ping }.verify
42
+ end
43
+
44
+ def test_verify_after_calling_stubs_does_nothing
45
+ mock = Mock.new do |m| m.stubs.ping end
46
+ mock.proxy.ping
47
+ mock.verify
48
+ end
49
+
50
+ def test_can_expect_and_call_method_with_no_arguments
51
+ mock = Mock.new do |m| m.expects.bing end
52
+ assert_nil mock.proxy.bing
53
+ end
54
+
55
+ def test_verify_after_calling_expected_method_with_no_args
56
+ mock = Mock.new do |m| m.expects.bing end
57
+ mock.proxy.bing
58
+ mock.verify
59
+ end
60
+
61
+ def test_verify_fails_when_expected_method_not_called
62
+ mock = Mock.new do |m| m.expects.bing end
63
+ assert_raise AssertionFailedError do mock.verify end
64
+ end
65
+
66
+ def test_can_use_mock_with_block_in_order_to_have_verify_called_automatically
67
+ mock = Mock.new do |m|
68
+ m.expects.some_method
69
+ m.expects.some_other_method
70
+ end
71
+ assert_raise AssertionFailedError do
72
+ mock.use { |o| o.some_method }
73
+ end
74
+ end
75
+
76
+ def test_can_call_a_stub_twice
77
+ mock = Mock.new do |m| m.stubs.bing.as {'crosby'} end
78
+ 2.times do assert_equal 'crosby', mock.proxy.bing end
79
+ end
80
+
81
+ def test_can_stub_a_method_with_an_argument
82
+ mock = Mock.new do |m| m.stubs.bing('who?').as {'crosby'} end
83
+ assert_equal 'crosby', mock.proxy.bing('who?')
84
+ end
85
+
86
+ def test_can_stub_a_method_twice_with_different_args
87
+ mock = Mock.new do |m|
88
+ m.stubs.last_name?('marlon').as {'brando'}
89
+ m.stubs.last_name?('jimmy').as {'cagney'}
90
+ end
91
+ assert_equal 'cagney', mock.proxy.last_name?('jimmy')
92
+ assert_equal 'brando', mock.proxy.last_name?('marlon')
93
+ end
94
+
95
+ def test_can_expect_a_method_with_an_argument
96
+ mock = Mock.new
97
+ mock.expects.who_am_i?('Jackie').as { 'Chan' }
98
+ mock.use do |o|
99
+ assert_equal 'Chan', o.who_am_i?('Jackie')
100
+ end
101
+ end
102
+
103
+ def test_error_raised_if_expected_method_called_twice
104
+ mock = Mock.new do |m| m.expects.some_method end
105
+ mock.proxy.some_method
106
+ assert_raise AssertionFailedError do mock.proxy.some_method end
107
+ end
108
+
109
+ def test_can_stub_pre_existing_methods
110
+ mock = Mock.new do |m| m.stubs.to_s.as {'foobar'} end
111
+ assert_equal 'foobar', mock.proxy.to_s
112
+ end
113
+
114
+ def test_can_stub_call_that_expects_a_block
115
+ mock = Mock.new do |m| m.stubs.each(Proc) end
116
+ mock.proxy.each {}
117
+ end
118
+
119
+ def test_error_raised_if_block_not_given_to_stubbed_method_wanting_a_block
120
+ mock = Mock.new do |m| m.stubs.each(Proc) end
121
+ assert_raise AssertionFailedError do
122
+ mock.proxy.each
123
+ end
124
+ end
125
+
126
+ def test_can_use_ranges_to_specify_argument_matches
127
+ mock = Mock.new do |m|
128
+ m.stubs.between_1_and_5?(1..5).as { true }
129
+ m.stubs.between_1_and_5?(6..10).as { false }
130
+ end
131
+ mock.use do |o|
132
+ assert_equal true, o.between_1_and_5?(3)
133
+ assert_equal false, o.between_1_and_5?(7)
134
+ end
135
+ end
136
+
137
+ def test_can_stub_constants
138
+ mock = Mock.new do |m| m.stubs.FOOBAR.as { "jimini" } end
139
+ assert_equal 'jimini', mock.proxy.FOOBAR
140
+ end
141
+
142
+ def test_can_expect_constants
143
+ mock = Mock.new do |m| m.expects.FOOBAR.as { "jimini" } end
144
+ assert_equal 'jimini', mock.proxy.FOOBAR
145
+ end
146
+
147
+ end
148
+
149
+ # class AutoMockTest < Test::Unit::TestCase
150
+ # include RMock::TestCaseHelpers
151
+ #
152
+ # def test_foo
153
+ # m = new_mock
154
+ # m.expects.foo.as { 'bar' }
155
+ # end
156
+ #
157
+ # end
158
+
159
+ # TODO:
160
+ # * Clearer error messages
161
+ # * Namespaces for constants
162
+ # * Intercepting top-level calls such as File#open
163
+ # * Explicit test for use of === in arg matching
164
+ # * Allow specification of call order
165
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: mockr
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2006-07-31 00:00:00 +02:00
8
+ summary: Easy Mock Objects for Ruby.
9
+ require_paths:
10
+ - lib
11
+ email:
12
+ homepage:
13
+ rubyforge_project:
14
+ description: "MockR is a tiny mock object library inspired by JMock. Main selling points: * Natural and unintrusive syntax * Supports the distinction between mocks and stubs * Constraint-based mechanism for matching call parameters See http://mockr.sanityinc.com/ for more info."
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors: []
30
+
31
+ files:
32
+ - lib/mockr.rb
33
+ - test/mockr_tests.rb
34
+ test_files: []
35
+
36
+ rdoc_options: []
37
+
38
+ extra_rdoc_files: []
39
+
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ requirements:
45
+ - none
46
+ dependencies: []
47
+