flexmock 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +17 -1
- data/README +84 -10
- data/Rakefile +11 -11
- data/TAGS +278 -195
- data/doc/releases/flexmock-0.8.0.rdoc +108 -0
- data/lib/flexmock/base.rb +1 -0
- data/lib/flexmock/core.rb +22 -15
- data/lib/flexmock/errors.rb +23 -0
- data/lib/flexmock/expectation.rb +38 -0
- data/lib/flexmock/expectation_director.rb +30 -10
- data/lib/flexmock/mock_container.rb +1 -1
- data/lib/flexmock/partial_mock.rb +8 -1
- data/lib/flexmock/rails/view_mocking.rb +30 -0
- data/lib/flexmock/rails.rb +15 -0
- data/lib/flexmock/undefined.rb +50 -0
- data/test/test_aliasing.rb +60 -0
- data/test/test_deprecated_methods.rb +1 -1
- data/test/test_examples_from_readme.rb +158 -0
- data/test/test_partial_mock.rb +42 -0
- data/test/test_rails_view_stub.rb +57 -0
- data/test/test_samples.rb +244 -0
- data/test/test_should_ignore_missing.rb +82 -0
- data/test/test_should_receive.rb +126 -3
- data/test/test_undefined.rb +88 -0
- metadata +13 -3
- data/test/test_example.rb +0 -36
@@ -0,0 +1,108 @@
|
|
1
|
+
= FlexMock 0.7.1 Released
|
2
|
+
|
3
|
+
FlexMock is a flexible mocking library for use in unit testing and
|
4
|
+
behavior specification in Ruby. It is a minor release that contains
|
5
|
+
some new funtionality.
|
6
|
+
|
7
|
+
== New Features n 0.8.0
|
8
|
+
|
9
|
+
* When should_ignore_missing is specified, the mock will return an
|
10
|
+
undefined object for any method calls that are ignored.
|
11
|
+
|
12
|
+
* A user can also specify an undefined object to be returned
|
13
|
+
explicitly by using FlexMock.undefined.
|
14
|
+
|
15
|
+
* Expectations may now be marked with "by_default". Default
|
16
|
+
expectations will be used unless a non-default expectation is given
|
17
|
+
for a matching method name.
|
18
|
+
|
19
|
+
* The :respond_to? method on mocks will now accept multiple arguments.
|
20
|
+
This eases the testing of models in certains situations.
|
21
|
+
|
22
|
+
* An experimental view mocker method is availble to mock out views in
|
23
|
+
Rails controller tests. See flexmock/rails/view_mocking.rb for more
|
24
|
+
details.
|
25
|
+
|
26
|
+
== What is FlexMock?
|
27
|
+
|
28
|
+
FlexMock is a flexible framework for creating mock object for testing. When
|
29
|
+
running unit tests, it is often desirable to use isolate the objects being
|
30
|
+
tested from the "real world" by having them interact with simplified test
|
31
|
+
objects. Sometimes these test objects simply return values when called, other
|
32
|
+
times they verify that certain methods were called with particular arguments
|
33
|
+
in a particular order.
|
34
|
+
|
35
|
+
FlexMock makes creating these test objects easy.
|
36
|
+
|
37
|
+
=== Features
|
38
|
+
|
39
|
+
* Easy integration with both Test::Unit and RSpec. Mocks created with the
|
40
|
+
flexmock method are automatically verified at the end of the test or
|
41
|
+
example.
|
42
|
+
|
43
|
+
* A fluent interface that allows mock behavior to be specified very
|
44
|
+
easily.
|
45
|
+
|
46
|
+
* A "record mode" where an existing implementation can record its
|
47
|
+
interaction with a mock for later validation against a new
|
48
|
+
implementation.
|
49
|
+
|
50
|
+
* Easy mocking of individual methods in existing, non-mock objects.
|
51
|
+
|
52
|
+
* Easy mocking of chains of method calls.
|
53
|
+
|
54
|
+
* The ability to cause classes to instantiate test instances (instead of real
|
55
|
+
instances) for the duration of a test.
|
56
|
+
|
57
|
+
=== Example
|
58
|
+
|
59
|
+
Suppose you had a Dog object that wagged a tail when it was happy.
|
60
|
+
Something like this:
|
61
|
+
|
62
|
+
class Dog
|
63
|
+
def initialize(a_tail)
|
64
|
+
@tail = a_tail
|
65
|
+
end
|
66
|
+
def happy
|
67
|
+
@tail.wag
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
To test the +Dog+ class without a real +Tail+ object (perhaps because
|
72
|
+
real +Tail+ objects activate servos in some robotic equipment), you
|
73
|
+
can do something like this:
|
74
|
+
|
75
|
+
require 'test/unit'
|
76
|
+
require 'flexmock/test_unit'
|
77
|
+
|
78
|
+
class TestDog < Test::Unit::TestCase
|
79
|
+
def test_dog_wags_tail_when_happy
|
80
|
+
tail = flexmock("tail")
|
81
|
+
tail.should_receive(:wag).once
|
82
|
+
dog = Dog.new(tail)
|
83
|
+
dog.happy
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
FlexMock will automatically verify that the mocked tail object received the
|
88
|
+
message +wag+ exactly one time. If it doesn't, the test will not pass.
|
89
|
+
|
90
|
+
See the FlexMock documentation at http://flexmock.rubyforge.org for details on
|
91
|
+
specifying arguments and return values on mocked methods, as well as a simple
|
92
|
+
technique for mocking tail objects when the Dog class creates the tail objects
|
93
|
+
directly.
|
94
|
+
|
95
|
+
== Availability
|
96
|
+
|
97
|
+
You can make sure you have the latest version with a quick RubyGems command:
|
98
|
+
|
99
|
+
gem install flexmock (you may need root/admin privileges)
|
100
|
+
|
101
|
+
Otherwise, you can get it from the more traditional places:
|
102
|
+
|
103
|
+
Download:: http://rubyforge.org/project/showfiles.php?group_id=170
|
104
|
+
|
105
|
+
You will find documentation at: http://flexmock.rubyforge.org.
|
106
|
+
|
107
|
+
-- Jim Weirich
|
108
|
+
|
data/lib/flexmock/base.rb
CHANGED
data/lib/flexmock/core.rb
CHANGED
@@ -9,6 +9,7 @@
|
|
9
9
|
# above copyright notice is included.
|
10
10
|
#+++
|
11
11
|
|
12
|
+
require 'flexmock/errors'
|
12
13
|
require 'flexmock/composite'
|
13
14
|
require 'flexmock/ordering'
|
14
15
|
|
@@ -45,13 +46,6 @@ require 'flexmock/ordering'
|
|
45
46
|
class FlexMock
|
46
47
|
include Ordering
|
47
48
|
|
48
|
-
# Error raised when flexmock is used incorrectly.
|
49
|
-
class UsageError < RuntimeError
|
50
|
-
end
|
51
|
-
|
52
|
-
class MockError < RuntimeError
|
53
|
-
end
|
54
|
-
|
55
49
|
attr_reader :flexmock_name
|
56
50
|
attr_accessor :flexmock_container
|
57
51
|
|
@@ -94,38 +88,50 @@ class FlexMock
|
|
94
88
|
end
|
95
89
|
alias mock_ignore_missing should_ignore_missing
|
96
90
|
|
91
|
+
def by_default
|
92
|
+
@last_expectation.by_default
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
97
96
|
# Handle missing methods by attempting to look up a handler.
|
98
97
|
def method_missing(sym, *args, &block)
|
99
98
|
flexmock_wrap do
|
100
99
|
if handler = @expectations[sym]
|
101
100
|
args << block if block_given?
|
102
101
|
handler.call(*args)
|
102
|
+
elsif @ignore_missing
|
103
|
+
FlexMock.undefined
|
103
104
|
else
|
104
|
-
super(sym, *args, &block)
|
105
|
+
super(sym, *args, &block)
|
105
106
|
end
|
106
107
|
end
|
107
108
|
end
|
108
109
|
|
109
110
|
# Save the original definition of respond_to? for use a bit later.
|
110
|
-
alias
|
111
|
+
alias flexmock_respond_to? respond_to?
|
111
112
|
|
112
113
|
# Override the built-in respond_to? to include the mocked methods.
|
113
|
-
def respond_to?(sym)
|
114
|
+
def respond_to?(sym, *args)
|
114
115
|
super || (@expectations[sym] ? true : @ignore_missing)
|
115
116
|
end
|
116
117
|
|
117
118
|
# Find the mock expectation for method sym and arguments.
|
118
|
-
def flexmock_find_expectation(
|
119
|
-
exp = @expectations[
|
119
|
+
def flexmock_find_expectation(method_name, *args) # :nodoc:
|
120
|
+
exp = @expectations[method_name]
|
120
121
|
exp ? exp.find_expectation(*args) : nil
|
121
122
|
end
|
122
123
|
|
124
|
+
# Return the expectation director for a method name.
|
125
|
+
def flexmock_expectations_for(method_name) # :nodoc:
|
126
|
+
@expectations[method_name]
|
127
|
+
end
|
128
|
+
|
123
129
|
# Override the built-in +method+ to include the mocked methods.
|
124
130
|
def method(sym)
|
125
131
|
@expectations[sym] || super
|
126
132
|
rescue NameError => ex
|
127
133
|
if @ignore_missing
|
128
|
-
proc { }
|
134
|
+
proc { FlexMock.undefined }
|
129
135
|
else
|
130
136
|
raise ex
|
131
137
|
end
|
@@ -151,13 +157,14 @@ class FlexMock
|
|
151
157
|
# See Expectation for a list of declarators that can be used.
|
152
158
|
#
|
153
159
|
def should_receive(*args)
|
154
|
-
ContainerHelper.parse_should_args(self, args) do |sym|
|
160
|
+
@last_expectation = ContainerHelper.parse_should_args(self, args) do |sym|
|
155
161
|
@expectations[sym] ||= ExpectationDirector.new(sym)
|
156
162
|
result = Expectation.new(self, sym)
|
157
163
|
@expectations[sym] << result
|
158
|
-
override_existing_method(sym) if
|
164
|
+
override_existing_method(sym) if flexmock_respond_to?(sym)
|
159
165
|
result
|
160
166
|
end
|
167
|
+
@last_expectation
|
161
168
|
end
|
162
169
|
|
163
170
|
# Declare that the mock object should expect methods by providing a
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#---
|
4
|
+
# Copyright 2003, 2004, 2005, 2006, 2007 by Jim Weirich (jim@weirichhouse.org).
|
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/noop'
|
13
|
+
|
14
|
+
class FlexMock
|
15
|
+
|
16
|
+
# Error raised when flexmock is used incorrectly.
|
17
|
+
class UsageError < ::RuntimeError
|
18
|
+
end
|
19
|
+
|
20
|
+
class MockError < ::RuntimeError
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/flexmock/expectation.rb
CHANGED
@@ -100,6 +100,11 @@ class FlexMock
|
|
100
100
|
@count_validators.all? { |v| v.eligible?(@actual_count) }
|
101
101
|
end
|
102
102
|
|
103
|
+
# Is this expectation constrained by any call counts?
|
104
|
+
def call_count_constrained?
|
105
|
+
! @count_validators.empty?
|
106
|
+
end
|
107
|
+
|
103
108
|
# Validate that the order
|
104
109
|
def validate_order
|
105
110
|
if @order_number
|
@@ -192,6 +197,34 @@ class FlexMock
|
|
192
197
|
end
|
193
198
|
alias :returns :and_return # :nodoc:
|
194
199
|
|
200
|
+
# Declare that the method returns and undefined object
|
201
|
+
# (FlexMock.undefined). Since the undefined object will always
|
202
|
+
# return itself for any message sent to it, it is a good "I don't
|
203
|
+
# care" value to return for methods that are commonly used in
|
204
|
+
# method chains.
|
205
|
+
#
|
206
|
+
# For example, if m.foo returns the undefined object, then:
|
207
|
+
#
|
208
|
+
# m.foo.bar.baz
|
209
|
+
#
|
210
|
+
# returns the undefined object without throwing an exception.
|
211
|
+
#
|
212
|
+
def and_return_undefined
|
213
|
+
and_return(FlexMock.undefined)
|
214
|
+
end
|
215
|
+
alias :returns_undefined :and_return_undefined
|
216
|
+
|
217
|
+
# :call-seq:
|
218
|
+
# and_yield(value1, value2, ...)
|
219
|
+
#
|
220
|
+
# Declare that the mocked method is expected to be given a block
|
221
|
+
# and that the block will be called with the values supplied to
|
222
|
+
# yield. If the mock is called multiple times, mulitple
|
223
|
+
# <tt>and_yield</tt> declarations can be used to supply different
|
224
|
+
# values on each call.
|
225
|
+
#
|
226
|
+
# An error is raised if the mocked method is not called with a
|
227
|
+
# block.
|
195
228
|
def and_yield(*yield_values)
|
196
229
|
@yield_queue << yield_values
|
197
230
|
end
|
@@ -352,6 +385,11 @@ class FlexMock
|
|
352
385
|
end
|
353
386
|
private :define_ordered
|
354
387
|
|
388
|
+
def by_default
|
389
|
+
expectations = mock.flexmock_expectations_for(@sym)
|
390
|
+
expectations.defaultify_expectation(self) if expectations
|
391
|
+
end
|
392
|
+
|
355
393
|
end
|
356
394
|
|
357
395
|
##########################################################################
|
@@ -10,9 +10,9 @@
|
|
10
10
|
#+++
|
11
11
|
|
12
12
|
require 'flexmock/noop'
|
13
|
+
require 'flexmock/errors'
|
13
14
|
|
14
15
|
class FlexMock
|
15
|
-
|
16
16
|
####################################################################
|
17
17
|
# The expectation director is responsible for routing calls to the
|
18
18
|
# correct expectations for a given argument list.
|
@@ -23,6 +23,7 @@ class FlexMock
|
|
23
23
|
def initialize(sym)
|
24
24
|
@sym = sym
|
25
25
|
@expectations = []
|
26
|
+
@defaults = []
|
26
27
|
@expected_order = nil
|
27
28
|
end
|
28
29
|
|
@@ -41,24 +42,43 @@ class FlexMock
|
|
41
42
|
exp.verify_call(*args)
|
42
43
|
end
|
43
44
|
|
44
|
-
# Find an expectation matching the given arguments.
|
45
|
-
def find_expectation(*args)
|
46
|
-
@expectations.find { |e| e.match_args(args) && e.eligible? } ||
|
47
|
-
@expectations.find { |e| e.match_args(args) }
|
48
|
-
end
|
49
|
-
|
50
45
|
# Append an expectation to this director.
|
51
46
|
def <<(expectation)
|
52
47
|
@expectations << expectation
|
53
48
|
end
|
54
49
|
|
50
|
+
# Find an expectation matching the given arguments.
|
51
|
+
def find_expectation(*args) # :nodoc:
|
52
|
+
find_expectation_in(@expectations, *args) ||
|
53
|
+
find_expectation_in(@defaults, *args)
|
54
|
+
end
|
55
|
+
|
55
56
|
# Do the post test verification for this directory. Check all the
|
56
|
-
# expectations.
|
57
|
-
|
58
|
-
|
57
|
+
# expectations. Only check the default expecatations if there are
|
58
|
+
# no non-default expectations.
|
59
|
+
def flexmock_verify # :nodoc:
|
60
|
+
(@expectations.empty? ? @defaults : @expectations).each do |exp|
|
59
61
|
exp.flexmock_verify
|
60
62
|
end
|
61
63
|
end
|
64
|
+
|
65
|
+
# Move the last defined expectation a default.
|
66
|
+
def defaultify_expectation(exp) # :nodoc:
|
67
|
+
last_exp = @expectations.last
|
68
|
+
if last_exp != exp
|
69
|
+
fail UsageError,
|
70
|
+
"Cannot make a previously defined expection into a default"
|
71
|
+
end
|
72
|
+
@expectations.pop
|
73
|
+
@defaults << exp
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def find_expectation_in(expectations, *args)
|
79
|
+
expectations.find { |e| e.match_args(args) && e.eligible? } ||
|
80
|
+
expectations.find { |e| e.match_args(args) }
|
81
|
+
end
|
62
82
|
end
|
63
83
|
|
64
84
|
end
|
@@ -44,7 +44,9 @@ class FlexMock
|
|
44
44
|
@methods_proxied = []
|
45
45
|
unless safe_mode
|
46
46
|
MOCK_METHODS.each do |sym|
|
47
|
-
|
47
|
+
unless @obj.respond_to?(sym)
|
48
|
+
add_mock_method(@obj, sym)
|
49
|
+
end
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
@@ -163,6 +165,11 @@ class FlexMock
|
|
163
165
|
def flexmock_container=(container)
|
164
166
|
end
|
165
167
|
|
168
|
+
# Forward the request for the expectation director to the mock.
|
169
|
+
def flexmock_expectations_for(method_name)
|
170
|
+
@mock.flexmock_expectations_for(method_name)
|
171
|
+
end
|
172
|
+
|
166
173
|
private
|
167
174
|
|
168
175
|
# The singleton class of the object.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'flexmock'
|
2
|
+
|
3
|
+
class FlexMock
|
4
|
+
module MockContainer
|
5
|
+
|
6
|
+
# Declare that the Rails controller under test should render the
|
7
|
+
# named view. If a view template name is given, it will be an
|
8
|
+
# error if the named view is not rendered during the execution of
|
9
|
+
# the contoller action. If no template name is given, then the
|
10
|
+
# any view may be rendered. If no view is actually rendered, then
|
11
|
+
# a assertion failure will occur.
|
12
|
+
def should_render_view(template_name=nil)
|
13
|
+
view = flexmock("MockView")
|
14
|
+
view.should_receive(
|
15
|
+
:assigns => {},
|
16
|
+
:render_file => true,
|
17
|
+
:first_render => "dummy_template"
|
18
|
+
)
|
19
|
+
if template_name
|
20
|
+
view.should_receive(:file_exists?).with(/#{template_name}$/).once.
|
21
|
+
and_return(true)
|
22
|
+
end
|
23
|
+
view.should_receive(:file_exists?).with(any).and_return(true)
|
24
|
+
view_class = flexmock("MockViewClasss")
|
25
|
+
view_class.should_receive(:new).and_return(view)
|
26
|
+
flexmock(@controller.class).should_receive(:view_class).once.
|
27
|
+
and_return(view_class)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#---
|
4
|
+
# Copyright 2003, 2004, 2005, 2006, 2007 by Jim Weirich (jim@weirichhouse.org).
|
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 the modules needed for rails oriented mocking.
|
13
|
+
|
14
|
+
require 'flexmock'
|
15
|
+
require 'flexmock/rails/view_mocking'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#---
|
4
|
+
# Copyright 2003, 2004, 2005, 2006, 2007 by Jim Weirich (jim@weirichhouse.org).
|
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
|
+
# Undefined is a self preserving undefined object. The result of
|
15
|
+
# any interaction with the undefined object will be the undefined
|
16
|
+
# object itself.
|
17
|
+
class Undefined
|
18
|
+
def method_missing(sym, *args, &block)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"-UNDEFINED-"
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def clone
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def coerce(other)
|
35
|
+
[FlexMock.undefined, FlexMock.undefined]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Single instance of undefined
|
40
|
+
@undefined = Undefined.new
|
41
|
+
|
42
|
+
# Undefined is normally available as FlexMock.undefined
|
43
|
+
def self.undefined
|
44
|
+
@undefined
|
45
|
+
end
|
46
|
+
|
47
|
+
class << Undefined
|
48
|
+
private :new
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'flexmock'
|
5
|
+
|
6
|
+
class FlexMock
|
7
|
+
module StubsAndExpects
|
8
|
+
def expects(*args)
|
9
|
+
result = should_receive(*args)
|
10
|
+
result.at_least.once unless result.call_count_constrained?
|
11
|
+
result
|
12
|
+
end
|
13
|
+
def stubs(*args)
|
14
|
+
should_receive(*args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module MockContainer
|
19
|
+
alias :mock :flexmock
|
20
|
+
alias :stub :flexmock
|
21
|
+
end
|
22
|
+
|
23
|
+
include StubsAndExpects
|
24
|
+
|
25
|
+
class PartialMockProxy
|
26
|
+
include StubsAndExpects
|
27
|
+
MOCK_METHODS << :stubs << :expects
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class AliasingTest < Test::Unit::TestCase
|
32
|
+
include FlexMock::TestCase
|
33
|
+
|
34
|
+
def test_mocking
|
35
|
+
m = mock("a cute dog").expects(:pat).twice.and_return(:woof!).mock
|
36
|
+
assert_equal :woof!, m.pat
|
37
|
+
assert_equal :woof!, m.pat
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_once_mocking
|
41
|
+
m = mock("a cute dog").expects(:pat).and_return(:woof!).mock
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_twice_mocking
|
45
|
+
m = mock("a cute dog").expects(:pat).and_return(:woof!).twice.mock
|
46
|
+
assert_raises(Test::Unit::AssertionFailedError) { m.flexmock_verify }
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_stubbing
|
50
|
+
m = stub("a cute dog").expects(:pat).and_return(:woof!).mock
|
51
|
+
assert_equal :woof!, m.pat
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_partial
|
55
|
+
obj = Object.new
|
56
|
+
stub(obj).stubs(:wag).and_return(:tail)
|
57
|
+
assert_equal :tail, obj.wag
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|