flexmock 0.7.1 → 0.8.0
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.
- 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
|
+
|