flexmock 1.2.0 → 1.3.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/Gemfile +3 -0
- data/Gemfile.lock +57 -0
- data/{README.rdoc → README.md} +376 -247
- data/Rakefile +5 -5
- data/doc/index.rdoc +31 -0
- data/doc/releases/flexmock-1.0.0.rdoc +2 -2
- data/doc/releases/flexmock-1.0.4.rdoc +165 -0
- data/doc/releases/flexmock-1.1.0.rdoc +144 -0
- data/doc/releases/flexmock-1.2.0.rdoc +150 -0
- data/doc/releases/flexmock-1.3.0.rdoc +165 -0
- data/lib/flexmock/core.rb +24 -3
- data/lib/flexmock/rspec_spy_matcher.rb +21 -3
- data/lib/flexmock/version.rb +1 -1
- data/test/rspec_integration/spy_example_spec.rb +73 -7
- data/test/spys_test.rb +53 -0
- metadata +14 -5
@@ -0,0 +1,165 @@
|
|
1
|
+
= FlexMock 1.3.0 Released
|
2
|
+
|
3
|
+
FlexMock is a flexible mocking library for use in unit testing and
|
4
|
+
behavior specification in Ruby. This release is a minor release with a
|
5
|
+
few bug fixes and some simple features.
|
6
|
+
|
7
|
+
== Changes in 1.3.0
|
8
|
+
|
9
|
+
=== Features
|
10
|
+
|
11
|
+
* Add 'and' and 'on' modifiers for the RSpec spy matcher.
|
12
|
+
|
13
|
+
* Add 'and' and 'on' options to the assert_spy_called test method.
|
14
|
+
|
15
|
+
* General documentation improvement.
|
16
|
+
|
17
|
+
=== Bug Fixes
|
18
|
+
|
19
|
+
* Fix bug in should_fail test helper that was not detecting failed
|
20
|
+
failures.
|
21
|
+
|
22
|
+
== Changes in 1.2.0
|
23
|
+
|
24
|
+
=== Features
|
25
|
+
|
26
|
+
* Add auto configure by requiring 'flexmock/rspec/configure'.
|
27
|
+
|
28
|
+
== Changes in 1.1.0
|
29
|
+
|
30
|
+
=== Features
|
31
|
+
|
32
|
+
* Add block support to pass_thru.
|
33
|
+
|
34
|
+
== Changes in 1.0.0
|
35
|
+
|
36
|
+
=== Features
|
37
|
+
|
38
|
+
* Mocks may now have a base class that limits what methods may be
|
39
|
+
mocked. This allows early detection of outdated mock setups when the
|
40
|
+
methods in the class are refactored.
|
41
|
+
|
42
|
+
* Spy assertions are now allowed. The verification of the calling of
|
43
|
+
mocked methods may now be done in the "then" portion of the test,
|
44
|
+
after the code under test has been run. This allows for much more
|
45
|
+
natural Given/When/Then style testing.
|
46
|
+
|
47
|
+
* A custom assert method (assert_spy_called) has been added to make
|
48
|
+
spy assertions easy when using Test::Unit or MiniTest.
|
49
|
+
|
50
|
+
* An RSpec matcher (have_received) has been added to make spy
|
51
|
+
assertions easy when using RSpec.
|
52
|
+
|
53
|
+
=== Bug Fixes
|
54
|
+
|
55
|
+
* Now correctly handling the mocking of meta-programmed methods.
|
56
|
+
|
57
|
+
* Using the documented +singleton_methods+ method.
|
58
|
+
|
59
|
+
* Accidently trying to partial mock a regular mock is now a no-op.
|
60
|
+
|
61
|
+
== What is FlexMock?
|
62
|
+
|
63
|
+
FlexMock is a flexible framework for creating mock object for testing.
|
64
|
+
When running unit tests, it is often desirable to use isolate the
|
65
|
+
objects being tested from the "real world" by having them interact
|
66
|
+
with simplified test objects. Sometimes these test objects simply
|
67
|
+
return values when called, other times they verify that certain
|
68
|
+
methods were called with particular arguments in a particular order.
|
69
|
+
|
70
|
+
FlexMock makes creating these test objects easy.
|
71
|
+
|
72
|
+
=== Features
|
73
|
+
|
74
|
+
* Easy integration with both Test::Unit and RSpec. Mocks created with the
|
75
|
+
flexmock method are automatically verified at the end of the test or
|
76
|
+
example.
|
77
|
+
|
78
|
+
* A fluent interface that allows mock behavior to be specified very
|
79
|
+
easily.
|
80
|
+
|
81
|
+
* A "record mode" where an existing implementation can record its
|
82
|
+
interaction with a mock for later validation against a new
|
83
|
+
implementation.
|
84
|
+
|
85
|
+
* Easy mocking of individual methods in existing, non-mock objects.
|
86
|
+
|
87
|
+
* Easy mocking of chains of method calls.
|
88
|
+
|
89
|
+
* The ability to cause classes to instantiate test instances (instead of real
|
90
|
+
instances) for the duration of a test.
|
91
|
+
|
92
|
+
=== Example
|
93
|
+
|
94
|
+
Suppose you had a Dog object that wagged a tail when it was happy.
|
95
|
+
Something like this:
|
96
|
+
|
97
|
+
class Dog
|
98
|
+
def initialize(a_tail)
|
99
|
+
@tail = a_tail
|
100
|
+
end
|
101
|
+
def happy
|
102
|
+
@tail.wag
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
To test the +Dog+ class without a real +Tail+ object (perhaps because
|
107
|
+
real +Tail+ objects activate servos in some robotic equipment), you
|
108
|
+
can do something like this:
|
109
|
+
|
110
|
+
RSpec.configure do |config|
|
111
|
+
config.mock_with :flexmock
|
112
|
+
end
|
113
|
+
|
114
|
+
describe Dog do
|
115
|
+
it "wags its tail when happy" do
|
116
|
+
tail = flexmock("tail")
|
117
|
+
tail.should_receive(:wag).once
|
118
|
+
dog = Dog.new(tail)
|
119
|
+
dog.happy
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
FlexMock will automatically verify that the mocked tail object received the
|
124
|
+
message +wag+ exactly one time. If it doesn't, the test will not pass.
|
125
|
+
|
126
|
+
Here's the same thing using the new spy support:
|
127
|
+
|
128
|
+
describe Dog do
|
129
|
+
it "wags its tail when happy" do
|
130
|
+
tail = flexmock("tail")
|
131
|
+
dog = Dog.new(tail)
|
132
|
+
dog.happy
|
133
|
+
tail.should have_received(:wag)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
This style works particularly well with the rspec-given library.
|
138
|
+
|
139
|
+
require 'rspec/given'
|
140
|
+
|
141
|
+
describe Dog do
|
142
|
+
context "when the dog is happy" do
|
143
|
+
Given(:tail) { flexmock(:on, Tail) }
|
144
|
+
Given(:dog) { Dog.new(tail) }
|
145
|
+
|
146
|
+
When { dog.happy }
|
147
|
+
|
148
|
+
Then { tail.should have_received(:wag) }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
See the FlexMock documentation at http://flexmock.rubyforge.org for details on
|
153
|
+
specifying arguments and return values on mocked methods, as well as a simple
|
154
|
+
technique for mocking tail objects when the Dog class creates the tail objects
|
155
|
+
directly.
|
156
|
+
|
157
|
+
== Availability
|
158
|
+
|
159
|
+
You can make sure you have the latest version with a quick RubyGems command:
|
160
|
+
|
161
|
+
gem install flexmock (you may need root/admin privileges)
|
162
|
+
|
163
|
+
You will find documentation at: http://flexmock.rubyforge.org.
|
164
|
+
|
165
|
+
-- Jim Weirich
|
data/lib/flexmock/core.rb
CHANGED
@@ -99,12 +99,26 @@ class FlexMock
|
|
99
99
|
self
|
100
100
|
end
|
101
101
|
|
102
|
-
CallRecord = Struct.new(:method_name, :args, :expectation)
|
102
|
+
CallRecord = Struct.new(:method_name, :args, :block_given, :expectation) do
|
103
|
+
def matches?(sym, actual_args, options)
|
104
|
+
method_name == sym &&
|
105
|
+
ArgumentMatching.all_match?(actual_args, args) &&
|
106
|
+
matches_block?(options[:with_block])
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def matches_block?(block_option)
|
112
|
+
block_option.nil? ||
|
113
|
+
(block_option && block_given) ||
|
114
|
+
(!block_option && !block_given)
|
115
|
+
end
|
116
|
+
end
|
103
117
|
|
104
118
|
# Handle missing methods by attempting to look up a handler.
|
105
119
|
def method_missing(sym, *args, &block)
|
106
120
|
enhanced_args = block_given? ? args + [block] : args
|
107
|
-
call_record = CallRecord.new(sym, enhanced_args)
|
121
|
+
call_record = CallRecord.new(sym, enhanced_args, block_given?)
|
108
122
|
@calls << call_record
|
109
123
|
flexmock_wrap do
|
110
124
|
if handler = @expectations[sym]
|
@@ -146,8 +160,15 @@ class FlexMock
|
|
146
160
|
# True if the mock received the given method and arguments.
|
147
161
|
def flexmock_received?(sym, args, options={})
|
148
162
|
count = 0
|
163
|
+
additional = options[:and] || []
|
164
|
+
additional = [additional] if additional.is_a?(Proc)
|
149
165
|
@calls.each { |call_record|
|
150
|
-
|
166
|
+
if call_record.matches?(sym, args, options)
|
167
|
+
count += 1
|
168
|
+
if options[:on_count].nil? || count == options[:on_count]
|
169
|
+
additional.each do |add| add.call(*call_record.args) end
|
170
|
+
end
|
171
|
+
end
|
151
172
|
}
|
152
173
|
if options[:times]
|
153
174
|
result = count == options[:times]
|
@@ -12,13 +12,12 @@ class FlexMock
|
|
12
12
|
@block = nil
|
13
13
|
@times = nil
|
14
14
|
@needs_block = nil
|
15
|
+
@additional_validations = []
|
15
16
|
end
|
16
17
|
|
17
18
|
def matches?(spy)
|
18
19
|
@spy = spy
|
19
|
-
@options =
|
20
|
-
@options[:times] = @times if @times
|
21
|
-
@options[:with_block] = @needs_block unless @needs_block.nil?
|
20
|
+
@options = construct_options
|
22
21
|
@spy.flexmock_received?(@method_name, @args, @options)
|
23
22
|
end
|
24
23
|
|
@@ -61,6 +60,25 @@ class FlexMock
|
|
61
60
|
def twice
|
62
61
|
times(2)
|
63
62
|
end
|
63
|
+
|
64
|
+
def on(on_count)
|
65
|
+
@on_count = on_count
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def and(&block)
|
70
|
+
@additional_validations << block
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def construct_options
|
75
|
+
{
|
76
|
+
:times => @times,
|
77
|
+
:with_block => @needs_block,
|
78
|
+
:on_count => @on_count,
|
79
|
+
:and => @additional_validations,
|
80
|
+
}
|
81
|
+
end
|
64
82
|
end
|
65
83
|
|
66
84
|
def have_received(method_name)
|
data/lib/flexmock/version.rb
CHANGED
@@ -22,23 +22,27 @@ describe "Dog" do
|
|
22
22
|
dog.wags(:tail)
|
23
23
|
end
|
24
24
|
|
25
|
-
it "
|
25
|
+
it "accepts no with" do
|
26
|
+
dog.should have_received(:wags)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "accepts with restriction" do
|
26
30
|
dog.should have_received(:wags).with(:tail)
|
27
31
|
end
|
28
32
|
|
29
|
-
it "
|
33
|
+
it "accepts not methods called" do
|
30
34
|
dog.should_not have_received(:bark)
|
31
35
|
end
|
32
36
|
|
33
|
-
it "rejects
|
37
|
+
it "rejects incorrect with restriction" do
|
34
38
|
should_fail(/^expected wag\(:foot\) to be received by <FlexMock:Dog Mock>/i) do
|
35
39
|
dog.should have_received(:wag).with(:foot)
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
39
|
-
it "rejects not
|
40
|
-
should_fail(/^expected
|
41
|
-
dog.should_not have_received(:
|
43
|
+
it "rejects not on correct matcher" do
|
44
|
+
should_fail(/^expected wags\(:tail\) to NOT be received by <FlexMock:Dog Mock>/i) do
|
45
|
+
dog.should_not have_received(:wags).with(:tail)
|
42
46
|
end
|
43
47
|
end
|
44
48
|
end
|
@@ -102,6 +106,65 @@ describe "Dog" do
|
|
102
106
|
end
|
103
107
|
end
|
104
108
|
|
109
|
+
context "with additional validations" do
|
110
|
+
it "accepts when correct" do
|
111
|
+
dog.wags(:tail)
|
112
|
+
dog.should have_received(:wags).and { |arg| arg.should == :tail }
|
113
|
+
end
|
114
|
+
|
115
|
+
it "rejects when incorrect" do
|
116
|
+
dog.wags(:tail)
|
117
|
+
should_fail(/expected: :foot.*got: :tail/im) do
|
118
|
+
dog.should have_received(:wags).and { |arg| arg.should == :foot }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "rejects on all calls" do
|
123
|
+
dog.wags(:foot)
|
124
|
+
dog.wags(:foot)
|
125
|
+
dog.wags(:tail)
|
126
|
+
should_fail(/expected: :foot.*got: :tail/im) do
|
127
|
+
dog.should have_received(:wags).and { |arg| arg.should == :foot }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it "runs the first additional block" do
|
132
|
+
dog.wags(:tail)
|
133
|
+
should_fail(/expected: :foot.*got: :tail/im) do
|
134
|
+
dog.should have_received(:wags).and { |args|
|
135
|
+
args.should == :foot
|
136
|
+
}.and { |args|
|
137
|
+
args.should == :tail
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it "runs the second additional block" do
|
143
|
+
dog.wags(:tail)
|
144
|
+
should_fail(/expected: :foot.*got: :tail/im) do
|
145
|
+
dog.should have_received(:wags).and { |args|
|
146
|
+
args.should == :tail
|
147
|
+
}.and { |args|
|
148
|
+
args.should == :foot
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "with ordinal constraints" do
|
155
|
+
it "detects the first call" do
|
156
|
+
dog.wags(:tail)
|
157
|
+
dog.wags(:foot)
|
158
|
+
dog.should have_received(:wags).and { |arg| arg.should == :tail }.on(1)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "detects the second call" do
|
162
|
+
dog.wags(:tail)
|
163
|
+
dog.wags(:foot)
|
164
|
+
dog.should have_received(:wags).and { |arg| arg.should == :foot }.on(2)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
105
168
|
context "with blocks" do
|
106
169
|
before do
|
107
170
|
dog.wags { }
|
@@ -126,16 +189,19 @@ describe "Dog" do
|
|
126
189
|
|
127
190
|
it "rejects barks with a block" do
|
128
191
|
should_fail(/with a block/) do
|
129
|
-
dog.should have_received(:
|
192
|
+
dog.should have_received(:barks).with_a_block
|
130
193
|
end
|
131
194
|
end
|
132
195
|
end
|
133
196
|
|
134
197
|
def should_fail(message_pattern)
|
198
|
+
failed = false
|
135
199
|
begin
|
136
200
|
yield
|
137
201
|
rescue RSpec::Expectations::ExpectationNotMetError => ex
|
202
|
+
failed = true
|
138
203
|
ex.message.should match(message_pattern)
|
139
204
|
end
|
205
|
+
RSpec::Expectations.fail_with "Expected block to fail with message #{message_pattern.inspect}, no failure detected" unless failed
|
140
206
|
end
|
141
207
|
end
|
data/test/spys_test.rb
CHANGED
@@ -90,6 +90,59 @@ class TestSpys < Test::Unit::TestCase
|
|
90
90
|
assert_spy_called @spy, :foo, Proc
|
91
91
|
end
|
92
92
|
|
93
|
+
def test_spy_accepts_correct_additional_validations
|
94
|
+
@spy.foo(2)
|
95
|
+
is_even = proc { |n| assert_equal 0, n%2 }
|
96
|
+
assert_spy_called @spy, { and: is_even }, :foo, Integer
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_spy_accepts_multiple_additional_validations_first_failing
|
100
|
+
@spy.foo(4)
|
101
|
+
is_two = proc { |n| assert_equal 2, n }
|
102
|
+
is_even = proc { |n| assert_equal 0, n%2 }
|
103
|
+
assert_failed(/2.*expected but was.*4/mi) do
|
104
|
+
assert_spy_called @spy, { and: [is_two, is_even] }, :foo, Integer
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_spy_accepts_multiple_additional_validations_second_failing
|
109
|
+
@spy.foo(4)
|
110
|
+
is_even = proc { |n| assert_equal 0, n%2 }
|
111
|
+
is_two = proc { |n| assert_equal 2, n }
|
112
|
+
assert_failed(/2.*expected but was.*4/mi) do
|
113
|
+
assert_spy_called @spy, { and: [is_even, is_two] }, :foo, Integer
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_spy_rejects_incorrect_additional_validations
|
118
|
+
@spy.foo(3)
|
119
|
+
is_even = proc { |n| assert_equal 0, n%2 }
|
120
|
+
assert_failed(/0.*expected but was.*1/mi) do
|
121
|
+
assert_spy_called @spy, { and: is_even }, :foo, Integer
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_spy_selectively_applies_additional_validations
|
126
|
+
@spy.foo(2)
|
127
|
+
@spy.foo(3)
|
128
|
+
@spy.foo(4)
|
129
|
+
is_even = proc { |n| assert_equal 0, n%2 }
|
130
|
+
assert_failed(/0.*expected but was.*1/mi) do
|
131
|
+
assert_spy_called @spy, { and: is_even, on: 2 }, :foo, Integer
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def assert_failed(message_pattern)
|
136
|
+
failed = false
|
137
|
+
begin
|
138
|
+
yield
|
139
|
+
rescue MiniTest::Assertion => ex
|
140
|
+
failed = true
|
141
|
+
assert_match message_pattern, ex.message
|
142
|
+
end
|
143
|
+
refute(!failed, "Expected block to fail")
|
144
|
+
end
|
145
|
+
|
93
146
|
def test_spy_methods_can_be_stubbed
|
94
147
|
@spy.should_receive(:foo).and_return(:hi)
|
95
148
|
result = @spy.foo
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flexmock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-27 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! "\n FlexMock is a extremely simple mock object class compatible\n
|
15
15
|
\ with the Test::Unit framework. Although the FlexMock's\n interface is
|
@@ -18,7 +18,7 @@ email: jim.weirich@gmail.com
|
|
18
18
|
executables: []
|
19
19
|
extensions: []
|
20
20
|
extra_rdoc_files:
|
21
|
-
-
|
21
|
+
- doc/index.rdoc
|
22
22
|
- CHANGES
|
23
23
|
- doc/GoogleExample.rdoc
|
24
24
|
- doc/releases/flexmock-0.4.0.rdoc
|
@@ -42,6 +42,10 @@ extra_rdoc_files:
|
|
42
42
|
- doc/releases/flexmock-0.9.0.rdoc
|
43
43
|
- doc/releases/flexmock-1.0.0.rdoc
|
44
44
|
- doc/releases/flexmock-1.0.3.rdoc
|
45
|
+
- doc/releases/flexmock-1.0.4.rdoc
|
46
|
+
- doc/releases/flexmock-1.1.0.rdoc
|
47
|
+
- doc/releases/flexmock-1.2.0.rdoc
|
48
|
+
- doc/releases/flexmock-1.3.0.rdoc
|
45
49
|
- doc/examples/rspec_examples_spec.rdoc
|
46
50
|
- doc/examples/test_unit_examples_test.rdoc
|
47
51
|
files:
|
@@ -49,7 +53,7 @@ files:
|
|
49
53
|
- Gemfile
|
50
54
|
- Gemfile.lock
|
51
55
|
- Rakefile
|
52
|
-
- README.
|
56
|
+
- README.md
|
53
57
|
- TAGS
|
54
58
|
- lib/flexmock/argument_matchers.rb
|
55
59
|
- lib/flexmock/argument_matching.rb
|
@@ -117,6 +121,7 @@ files:
|
|
117
121
|
- test/undefined_test.rb
|
118
122
|
- flexmock.blurb
|
119
123
|
- install.rb
|
124
|
+
- doc/index.rdoc
|
120
125
|
- doc/GoogleExample.rdoc
|
121
126
|
- doc/releases/flexmock-0.4.0.rdoc
|
122
127
|
- doc/releases/flexmock-0.4.1.rdoc
|
@@ -139,6 +144,10 @@ files:
|
|
139
144
|
- doc/releases/flexmock-0.9.0.rdoc
|
140
145
|
- doc/releases/flexmock-1.0.0.rdoc
|
141
146
|
- doc/releases/flexmock-1.0.3.rdoc
|
147
|
+
- doc/releases/flexmock-1.0.4.rdoc
|
148
|
+
- doc/releases/flexmock-1.1.0.rdoc
|
149
|
+
- doc/releases/flexmock-1.2.0.rdoc
|
150
|
+
- doc/releases/flexmock-1.3.0.rdoc
|
142
151
|
- doc/examples/rspec_examples_spec.rdoc
|
143
152
|
- doc/examples/test_unit_examples_test.rdoc
|
144
153
|
homepage: https://github.com/jimweirich/flexmock
|
@@ -160,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
160
169
|
version: '0'
|
161
170
|
segments:
|
162
171
|
- 0
|
163
|
-
hash: -
|
172
|
+
hash: -2506978520909989178
|
164
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
174
|
none: false
|
166
175
|
requirements:
|