flexmock 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|