flexmock 0.9.0 → 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +325 -177
- data/Rakefile +22 -8
- data/TAGS +772 -669
- data/doc/releases/flexmock-1.0.0.rdoc +128 -0
- data/lib/flexmock.rb +1 -1
- data/lib/flexmock/argument_matchers.rb +16 -3
- data/lib/flexmock/argument_matching.rb +33 -0
- data/lib/flexmock/argument_types.rb +5 -1
- data/lib/flexmock/base.rb +1 -1
- data/lib/flexmock/composite.rb +3 -3
- data/lib/flexmock/core.rb +32 -2
- data/lib/flexmock/core_class_methods.rb +1 -1
- data/lib/flexmock/default_framework_adapter.rb +1 -1
- data/lib/flexmock/deprecated_methods.rb +1 -1
- data/lib/flexmock/errors.rb +1 -1
- data/lib/flexmock/expectation.rb +15 -13
- data/lib/flexmock/expectation_director.rb +1 -1
- data/lib/flexmock/explicit_needed.rb +39 -0
- data/lib/flexmock/mock_container.rb +8 -2
- data/lib/flexmock/noop.rb +1 -1
- data/lib/flexmock/ordering.rb +1 -1
- data/lib/flexmock/partial_mock.rb +12 -2
- data/lib/flexmock/rails.rb +1 -1
- data/lib/flexmock/recorder.rb +1 -1
- data/lib/flexmock/rspec.rb +5 -1
- data/lib/flexmock/rspec_spy_matcher.rb +74 -0
- data/lib/flexmock/spy_describers.rb +60 -0
- data/lib/flexmock/test_unit.rb +1 -1
- data/lib/flexmock/test_unit_assert_spy_called.rb +34 -0
- data/lib/flexmock/test_unit_integration.rb +3 -1
- data/lib/flexmock/undefined.rb +1 -1
- data/lib/flexmock/validators.rb +1 -1
- data/lib/flexmock/version.rb +4 -2
- data/test/assert_spy_called_test.rb +89 -0
- data/test/container_methods_test.rb +1 -1
- data/test/default_framework_adapter_test.rb +1 -1
- data/test/deprecated_methods_test.rb +1 -1
- data/test/examples_from_readme_test.rb +1 -1
- data/test/extended_should_receive_test.rb +1 -1
- data/test/naming_test.rb +1 -2
- data/test/new_instances_test.rb +1 -3
- data/test/partial_mock_test.rb +7 -1
- data/test/record_mode_test.rb +1 -1
- data/test/rspec_integration/integration_spec.rb +11 -3
- data/test/rspec_integration/spy_example_spec.rb +141 -0
- data/test/samples_test.rb +1 -1
- data/test/should_ignore_missing_test.rb +6 -2
- data/test/should_receive_test.rb +31 -2
- data/test/spys_test.rb +148 -0
- data/test/test_unit_integration/auto_test_unit_test.rb +1 -1
- data/test/tu_integration_test.rb +1 -1
- data/test/undefined_test.rb +1 -1
- metadata +16 -8
@@ -0,0 +1,128 @@
|
|
1
|
+
= FlexMock 1.0.0 Released
|
2
|
+
|
3
|
+
FlexMock is a flexible mocking library for use in unit testing and
|
4
|
+
behavior specification in Ruby. Release 1.0.0 is a minor release with
|
5
|
+
a few bug fixes.
|
6
|
+
|
7
|
+
== Changes in 1.0.0
|
8
|
+
|
9
|
+
* Mocks may now have a base class that limits what methods may be
|
10
|
+
mocked. This allows early detection of outdated mock setups when the
|
11
|
+
methods in the class are refactored.
|
12
|
+
|
13
|
+
* Spy assertions are now allowed. The verification of the calling of
|
14
|
+
mocked methods may now be done in the "then" portion of the test,
|
15
|
+
after the code under test has been run. This allows for much more
|
16
|
+
natural Given/When/Then style testing.
|
17
|
+
|
18
|
+
* A custom assert method (assert_spy_called) has been added to make
|
19
|
+
spy assertions easy when using Test::Unit or MiniTest.
|
20
|
+
|
21
|
+
* An RSpec matcher (have_received) has been added to make spy
|
22
|
+
assertions easy when using RSpec.
|
23
|
+
|
24
|
+
== What is FlexMock?
|
25
|
+
|
26
|
+
FlexMock is a flexible framework for creating mock object for testing. When
|
27
|
+
running unit tests, it is often desirable to use isolate the objects being
|
28
|
+
tested from the "real world" by having them interact with simplified test
|
29
|
+
objects. Sometimes these test objects simply return values when called, other
|
30
|
+
times they verify that certain methods were called with particular arguments
|
31
|
+
in a particular order.
|
32
|
+
|
33
|
+
FlexMock makes creating these test objects easy.
|
34
|
+
|
35
|
+
=== Features
|
36
|
+
|
37
|
+
* Easy integration with both Test::Unit and RSpec. Mocks created with the
|
38
|
+
flexmock method are automatically verified at the end of the test or
|
39
|
+
example.
|
40
|
+
|
41
|
+
* A fluent interface that allows mock behavior to be specified very
|
42
|
+
easily.
|
43
|
+
|
44
|
+
* A "record mode" where an existing implementation can record its
|
45
|
+
interaction with a mock for later validation against a new
|
46
|
+
implementation.
|
47
|
+
|
48
|
+
* Easy mocking of individual methods in existing, non-mock objects.
|
49
|
+
|
50
|
+
* Easy mocking of chains of method calls.
|
51
|
+
|
52
|
+
* The ability to cause classes to instantiate test instances (instead of real
|
53
|
+
instances) for the duration of a test.
|
54
|
+
|
55
|
+
=== Example
|
56
|
+
|
57
|
+
Suppose you had a Dog object that wagged a tail when it was happy.
|
58
|
+
Something like this:
|
59
|
+
|
60
|
+
class Dog
|
61
|
+
def initialize(a_tail)
|
62
|
+
@tail = a_tail
|
63
|
+
end
|
64
|
+
def happy
|
65
|
+
@tail.wag
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
To test the +Dog+ class without a real +Tail+ object (perhaps because
|
70
|
+
real +Tail+ objects activate servos in some robotic equipment), you
|
71
|
+
can do something like this:
|
72
|
+
|
73
|
+
RSpec.configure do |config|
|
74
|
+
config.mock_with :flexmock
|
75
|
+
end
|
76
|
+
|
77
|
+
describe Dog do
|
78
|
+
it "wags its tail when happy" do
|
79
|
+
tail = flexmock("tail")
|
80
|
+
tail.should_receive(:wag).once
|
81
|
+
dog = Dog.new(tail)
|
82
|
+
dog.happy
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
FlexMock will automatically verify that the mocked tail object received the
|
87
|
+
message +wag+ exactly one time. If it doesn't, the test will not pass.
|
88
|
+
|
89
|
+
Here's the same thing using the new spy support:
|
90
|
+
|
91
|
+
describe Dog do
|
92
|
+
it "wags its tail when happy" do
|
93
|
+
tail = flexmock("tail")
|
94
|
+
dog = Dog.new(tail)
|
95
|
+
dog.happy
|
96
|
+
tail.should have_received(:wag)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
This style works particularly well with the rspec-given library.
|
101
|
+
|
102
|
+
require 'rspec/given'
|
103
|
+
|
104
|
+
describe Dog do
|
105
|
+
context "when the dog is happy" do
|
106
|
+
Given(:tail) { flexmock(:on, Tail) }
|
107
|
+
Given(:dog) { Dog.new(tail) }
|
108
|
+
|
109
|
+
When { dog.happy }
|
110
|
+
|
111
|
+
Then { tail.should have_received(:wag) }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
See the FlexMock documentation at http://flexmock.rubyforge.org for details on
|
116
|
+
specifying arguments and return values on mocked methods, as well as a simple
|
117
|
+
technique for mocking tail objects when the Dog class creates the tail objects
|
118
|
+
directly.
|
119
|
+
|
120
|
+
== Availability
|
121
|
+
|
122
|
+
You can make sure you have the latest version with a quick RubyGems command:
|
123
|
+
|
124
|
+
gem install flexmock (you may need root/admin privileges)
|
125
|
+
|
126
|
+
You will find documentation at: http://flexmock.rubyforge.org.
|
127
|
+
|
128
|
+
-- Jim Weirich
|
data/lib/flexmock.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
#---
|
4
|
-
# Copyright 2003-
|
4
|
+
# Copyright 2003-2012 by Jim Weirich (jim.weirich@gmail.com).
|
5
5
|
# All rights reserved.
|
6
6
|
#
|
7
7
|
# Permission is granted for use, copying, modification, distribution,
|
@@ -54,7 +54,7 @@ class FlexMock
|
|
54
54
|
end
|
55
55
|
|
56
56
|
####################################################################
|
57
|
-
# Match
|
57
|
+
# Match hashes that match all the fields of +hash+.
|
58
58
|
class HashMatcher
|
59
59
|
def initialize(hash)
|
60
60
|
@hash = hash
|
@@ -68,7 +68,7 @@ class FlexMock
|
|
68
68
|
end
|
69
69
|
|
70
70
|
####################################################################
|
71
|
-
# Match
|
71
|
+
# Match objects that implement all the methods in +methods+.
|
72
72
|
class DuckMatcher
|
73
73
|
def initialize(methods)
|
74
74
|
@methods = methods
|
@@ -81,5 +81,18 @@ class FlexMock
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
+
####################################################################
|
85
|
+
# Match objects that implement all the methods in +methods+.
|
86
|
+
class OptionalProcMatcher
|
87
|
+
def initialize
|
88
|
+
end
|
89
|
+
def ===(target)
|
90
|
+
ArgumentMatching.missing?(target) || Proc === target
|
91
|
+
end
|
92
|
+
def inspect
|
93
|
+
"optional_proc"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
OPTIONAL_PROC_MATCHER = OptionalProcMatcher.new
|
84
97
|
|
85
98
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class FlexMock
|
2
|
+
module ArgumentMatching
|
3
|
+
module_function
|
4
|
+
|
5
|
+
MISSING_ARG = Object.new
|
6
|
+
|
7
|
+
def all_match?(expected_args, actual_args)
|
8
|
+
return true if expected_args.nil?
|
9
|
+
return false if actual_args.size > expected_args.size
|
10
|
+
i = 0
|
11
|
+
while i < actual_args.size
|
12
|
+
return false unless match?(expected_args[i], actual_args[i])
|
13
|
+
i += 1
|
14
|
+
end
|
15
|
+
while i < expected_args.size
|
16
|
+
return false unless match?(expected_args[i], MISSING_ARG)
|
17
|
+
i += 1
|
18
|
+
end
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
# Does the expected argument match the corresponding actual value.
|
23
|
+
def match?(expected, actual)
|
24
|
+
expected === actual ||
|
25
|
+
expected == actual ||
|
26
|
+
( Regexp === expected && expected === actual.to_s )
|
27
|
+
end
|
28
|
+
|
29
|
+
def missing?(arg)
|
30
|
+
arg == MISSING_ARG
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
#---
|
4
|
-
# Copyright 2003-
|
4
|
+
# Copyright 2003-2012 by Jim Weirich (jim.weirich@gmail.com).
|
5
5
|
# All rights reserved.
|
6
6
|
|
7
7
|
# Permission is granted for use, copying, modification, distribution,
|
@@ -48,6 +48,10 @@ class FlexMock
|
|
48
48
|
def ducktype(*methods)
|
49
49
|
DuckMatcher.new(methods)
|
50
50
|
end
|
51
|
+
|
52
|
+
def optional_proc
|
53
|
+
OPTIONAL_PROC_MATCHER
|
54
|
+
end
|
51
55
|
end
|
52
56
|
extend ArgumentTypes
|
53
57
|
|
data/lib/flexmock/base.rb
CHANGED
data/lib/flexmock/composite.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Copyright 2003-2012 by Jim Weirich (jim.weirich@gmail.com).
|
4
|
+
# All rights reserved.
|
5
5
|
|
6
6
|
require 'flexmock/noop'
|
7
7
|
|
8
8
|
class FlexMock
|
9
9
|
|
10
|
-
end
|
10
|
+
end
|
data/lib/flexmock/core.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
#---
|
4
|
-
# Copyright 2003-
|
4
|
+
# Copyright 2003-2012 by Jim Weirich (jim.weirich@gmail.com).
|
5
5
|
# All rights reserved.
|
6
6
|
#
|
7
7
|
# Permission is granted for use, copying, modification, distribution,
|
@@ -12,6 +12,8 @@
|
|
12
12
|
require 'flexmock/errors'
|
13
13
|
require 'flexmock/composite'
|
14
14
|
require 'flexmock/ordering'
|
15
|
+
require 'flexmock/argument_matching'
|
16
|
+
require 'flexmock/explicit_needed'
|
15
17
|
|
16
18
|
######################################################################
|
17
19
|
# FlexMock is a flexible mock object framework for supporting testing.
|
@@ -57,6 +59,8 @@ class FlexMock
|
|
57
59
|
@expectations = Hash.new
|
58
60
|
@ignore_missing = false
|
59
61
|
@verified = false
|
62
|
+
@calls = []
|
63
|
+
@base_class = nil
|
60
64
|
container = UseContainer.new if container.nil?
|
61
65
|
container.flexmock_remember(self)
|
62
66
|
end
|
@@ -85,6 +89,7 @@ class FlexMock
|
|
85
89
|
# Ignore all undefined (missing) method calls.
|
86
90
|
def should_ignore_missing
|
87
91
|
@ignore_missing = true
|
92
|
+
self
|
88
93
|
end
|
89
94
|
alias mock_ignore_missing should_ignore_missing
|
90
95
|
|
@@ -95,10 +100,13 @@ class FlexMock
|
|
95
100
|
|
96
101
|
# Handle missing methods by attempting to look up a handler.
|
97
102
|
def method_missing(sym, *args, &block)
|
103
|
+
@calls << [sym, block_given? ? args + [block] : args]
|
98
104
|
flexmock_wrap do
|
99
105
|
if handler = @expectations[sym]
|
100
106
|
args << block if block_given?
|
101
107
|
handler.call(*args)
|
108
|
+
elsif @base_class && @base_class.instance_methods.include?(sym)
|
109
|
+
FlexMock.undefined
|
102
110
|
elsif @ignore_missing
|
103
111
|
FlexMock.undefined
|
104
112
|
else
|
@@ -126,6 +134,27 @@ class FlexMock
|
|
126
134
|
@expectations[method_name]
|
127
135
|
end
|
128
136
|
|
137
|
+
def flexmock_spies_on(base_class)
|
138
|
+
@base_class = base_class
|
139
|
+
end
|
140
|
+
|
141
|
+
def flexmock_was_called_with?(sym, args, options={})
|
142
|
+
count = 0
|
143
|
+
@calls.each { |call_sym, call_args, call_block|
|
144
|
+
count += 1 if (call_sym == sym) && ArgumentMatching.all_match?(args, call_args)
|
145
|
+
}
|
146
|
+
if options[:times]
|
147
|
+
result = count == options[:times]
|
148
|
+
else
|
149
|
+
result = count > 0
|
150
|
+
end
|
151
|
+
result
|
152
|
+
end
|
153
|
+
|
154
|
+
def flexmock_invoke_original(sym, args)
|
155
|
+
return FlexMock.undefined
|
156
|
+
end
|
157
|
+
|
129
158
|
# Override the built-in +method+ to include the mocked methods.
|
130
159
|
def method(sym)
|
131
160
|
@expectations[sym] || super
|
@@ -162,9 +191,10 @@ class FlexMock
|
|
162
191
|
result = Expectation.new(self, sym)
|
163
192
|
@expectations[sym] << result
|
164
193
|
override_existing_method(sym) if flexmock_respond_to?(sym)
|
194
|
+
result = ExplicitNeeded.new(result, sym, @base_class) if
|
195
|
+
@base_class && ! @base_class.instance_methods.include?(sym)
|
165
196
|
result
|
166
197
|
end
|
167
|
-
@last_expectation
|
168
198
|
end
|
169
199
|
|
170
200
|
# Declare that the mock object should expect methods by providing a
|
data/lib/flexmock/errors.rb
CHANGED
data/lib/flexmock/expectation.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
#---
|
4
|
-
# Copyright 2003-
|
4
|
+
# Copyright 2003-2012 by Jim Weirich (jim.weirich@gmail.com).
|
5
5
|
# All rights reserved.
|
6
6
|
|
7
7
|
# Permission is granted for use, copying, modification, distribution,
|
@@ -10,6 +10,7 @@
|
|
10
10
|
#+++
|
11
11
|
|
12
12
|
require 'flexmock/noop'
|
13
|
+
require 'flexmock/argument_matching'
|
13
14
|
|
14
15
|
class FlexMock
|
15
16
|
|
@@ -127,18 +128,7 @@ class FlexMock
|
|
127
128
|
# Does the argument list match this expectation's argument
|
128
129
|
# specification.
|
129
130
|
def match_args(args)
|
130
|
-
|
131
|
-
# return false if @expected_args.nil?
|
132
|
-
return true if @expected_args.nil?
|
133
|
-
return false if args.size != @expected_args.size
|
134
|
-
(0...args.size).all? { |i| match_arg(@expected_args[i], args[i]) }
|
135
|
-
end
|
136
|
-
|
137
|
-
# Does the expected argument match the corresponding actual value.
|
138
|
-
def match_arg(expected, actual)
|
139
|
-
expected === actual ||
|
140
|
-
expected == actual ||
|
141
|
-
( Regexp === expected && expected === actual.to_s )
|
131
|
+
ArgumentMatching.all_match?(@expected_args, args)
|
142
132
|
end
|
143
133
|
|
144
134
|
# Declare that the method should expect the given argument list.
|
@@ -268,6 +258,12 @@ class FlexMock
|
|
268
258
|
end
|
269
259
|
alias :throws :and_throw
|
270
260
|
|
261
|
+
def pass_thru
|
262
|
+
and_return { |*args|
|
263
|
+
@mock.flexmock_invoke_original(@sym, args)
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
271
267
|
# Declare that the method may be called any number of times.
|
272
268
|
def zero_or_more_times
|
273
269
|
at_least.never
|
@@ -385,6 +381,12 @@ class FlexMock
|
|
385
381
|
end
|
386
382
|
private :define_ordered
|
387
383
|
|
384
|
+
# No-op for allowing explicit calls when explicit not explicitly
|
385
|
+
# needed.
|
386
|
+
def explicit
|
387
|
+
self
|
388
|
+
end
|
389
|
+
|
388
390
|
def by_default
|
389
391
|
expectations = mock.flexmock_expectations_for(@sym)
|
390
392
|
expectations.defaultify_expectation(self) if expectations
|