rr 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 +6 -0
- data/README.rdoc +6 -0
- data/Rakefile +27 -48
- data/VERSION.yml +4 -0
- data/lib/rr/adapters/rspec.rb +6 -1
- data/lib/rr/double_definitions/double_definition.rb +2 -1
- data/lib/rr/wildcard_matchers.rb +158 -0
- data/spec/rr/rspec/invocation_matcher_spec.rb +20 -0
- data/spec/rr/test_unit/test_unit_backtrace_test.rb +1 -0
- metadata +6 -2
data/CHANGES
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
- Fixed compatability issues with Ruby 1.9
|
2
|
+
- Aliased any_number_of_times with any_times
|
3
|
+
- Better error messages for have_received and assert_received matchers (Patch by Joe Ferris)
|
4
|
+
- Better documentation on RR wilcard matchers (Patch by Phil Arnowsky)
|
5
|
+
|
6
|
+
* 0.7.1
|
1
7
|
- Performance improvements
|
2
8
|
|
3
9
|
* 0.7.0
|
data/README.rdoc
CHANGED
@@ -301,6 +301,10 @@ In RR, you would do:
|
|
301
301
|
mock(object).foobar(satisfy {|arg| arg.length == 2})
|
302
302
|
object.foobar("xy")
|
303
303
|
|
304
|
+
==== Writing your own Argument Matchers
|
305
|
+
Writing a custom argument wildcard matcher is not difficult. See
|
306
|
+
RR::WildcardMatchers for details.
|
307
|
+
|
304
308
|
=== Invocation Amount Wildcard Matchers
|
305
309
|
==== any_times
|
306
310
|
mock(object).method_name(anything).times(any_times) {return_value}
|
@@ -308,6 +312,7 @@ In RR, you would do:
|
|
308
312
|
== Special Thanks To
|
309
313
|
With any development effort, there are countless people who have contributed
|
310
314
|
to making it possible. We all are standing on the shoulders of giants.
|
315
|
+
* Andreas Haller for patches
|
311
316
|
* Aslak Hellesoy for Developing Rspec
|
312
317
|
* Dan North for syntax ideas
|
313
318
|
* Dave Astels for some BDD inspiration
|
@@ -325,5 +330,6 @@ to making it possible. We all are standing on the shoulders of giants.
|
|
325
330
|
* Nick Kallen for documentation suggestions, bug reports, and patches
|
326
331
|
* Nathan Sobo for various ideas and inspiration for cleaner and more expressive code
|
327
332
|
* Parker Thompson for pairing with me
|
333
|
+
* Phil Arnowsky for patches
|
328
334
|
* Pivotal Labs for sponsoring RR development
|
329
335
|
* Stephen Baker for Developing Rspec
|
data/Rakefile
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "rake"
|
2
|
-
require 'rake/gempackagetask'
|
3
2
|
require 'rake/contrib/rubyforgepublisher'
|
4
3
|
require 'rake/clean'
|
5
4
|
require 'rake/testtask'
|
@@ -15,56 +14,36 @@ task(:spec) do
|
|
15
14
|
run_suite
|
16
15
|
end
|
17
16
|
|
18
|
-
desc "Copies the trunk to a tag with the name of the current release"
|
19
|
-
task(:tag_release) do
|
20
|
-
tag_release
|
21
|
-
end
|
22
|
-
|
23
17
|
def run_suite
|
24
18
|
dir = File.dirname(__FILE__)
|
25
19
|
system("ruby #{dir}/spec/spec_suite.rb") || raise("Spec Suite failed")
|
26
20
|
end
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
s.author = "Brian Takita"
|
56
|
-
s.email = "brian@pivotallabs.com"
|
57
|
-
s.homepage = "http://pivotallabs.com"
|
58
|
-
s.rubyforge_project = "pivotalrb"
|
59
|
-
end
|
60
|
-
|
61
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
62
|
-
pkg.need_zip = true
|
63
|
-
pkg.need_tar = true
|
22
|
+
begin
|
23
|
+
require 'jeweler'
|
24
|
+
Jeweler::Tasks.new do |s|
|
25
|
+
s.name = "rr"
|
26
|
+
s.summary = "RR (Double Ruby) is a double framework that features a rich " <<
|
27
|
+
"selection of double techniques and a terse syntax. " <<
|
28
|
+
"http://xunitpatterns.com/Test%20Double.html"
|
29
|
+
s.email = "brian@pivotallabs.com"
|
30
|
+
s.homepage = "http://pivotallabs.com"
|
31
|
+
s.description = "RR (Double Ruby) is a double framework that features a rich " <<
|
32
|
+
"selection of double techniques and a terse syntax. " <<
|
33
|
+
"http://xunitpatterns.com/Test%20Double.html"
|
34
|
+
s.authors = ["Brian Takita"]
|
35
|
+
s.files = FileList[
|
36
|
+
'[A-Z]*',
|
37
|
+
'*.rb',
|
38
|
+
'lib/**/*.rb',
|
39
|
+
'spec/**/*.rb'
|
40
|
+
].to_a
|
41
|
+
s.test_files = Dir.glob('spec/*_spec.rb')
|
42
|
+
s.has_rdoc = true
|
43
|
+
s.extra_rdoc_files = [ "README.rdoc", "CHANGES" ]
|
44
|
+
s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers"]
|
45
|
+
s.rubyforge_project = "pivotalrb"
|
46
|
+
end
|
47
|
+
rescue LoadError
|
48
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
64
49
|
end
|
65
|
-
|
66
|
-
def tag_release
|
67
|
-
dashed_version = PKG_VERSION.gsub('.', '-')
|
68
|
-
svn_user = "#{ENV["SVN_USER"]}@" || ""
|
69
|
-
`svn cp svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/rr/trunk svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/rr/tags/REL-#{dashed_version} -m 'Version #{PKG_VERSION}'`
|
70
|
-
end
|
data/VERSION.yml
ADDED
data/lib/rr/adapters/rspec.rb
CHANGED
@@ -32,7 +32,12 @@ module RR
|
|
32
32
|
|
33
33
|
def matches?(subject)
|
34
34
|
@verification.subject = subject
|
35
|
-
RR::Space.instance.recorded_calls.match_error(@verification)
|
35
|
+
if error = RR::Space.instance.recorded_calls.match_error(@verification)
|
36
|
+
@failure_message = error.message
|
37
|
+
false
|
38
|
+
else
|
39
|
+
true
|
40
|
+
end
|
36
41
|
end
|
37
42
|
|
38
43
|
def nil?
|
@@ -160,6 +160,7 @@ module RR
|
|
160
160
|
install_method_callback return_value_block
|
161
161
|
self
|
162
162
|
end
|
163
|
+
alias_method :any_times, :any_number_of_times
|
163
164
|
|
164
165
|
# Double#times creates an TimesCalledExpectation of the passed
|
165
166
|
# in number.
|
@@ -251,7 +252,7 @@ module RR
|
|
251
252
|
if implementation
|
252
253
|
implemented_by implementation
|
253
254
|
else
|
254
|
-
implemented_by
|
255
|
+
implemented_by proc {value}
|
255
256
|
end
|
256
257
|
self
|
257
258
|
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
= Writing your own custom wildcard matchers.
|
4
|
+
Writing new wildcard matchers is not too difficult. If you've ever written
|
5
|
+
a custom expectation in RSpec, the implementation is very similar.
|
6
|
+
|
7
|
+
As an example, let's say that you want a matcher that will match any number
|
8
|
+
divisible by a certain integer. In use, it might look like this:
|
9
|
+
|
10
|
+
# Will pass if BananaGrabber#bunch_bananas is called with an integer
|
11
|
+
# divisible by 5.
|
12
|
+
|
13
|
+
mock(BananaGrabber).bunch_bananas(divisible_by(5))
|
14
|
+
|
15
|
+
To implement this, we need a class RR::WildcardMatchers::DivisibleBy with
|
16
|
+
these instance methods:
|
17
|
+
|
18
|
+
* ==(other)
|
19
|
+
* eql?(other) (usually aliased to #==)
|
20
|
+
* inspect
|
21
|
+
* wildcard_match?(other)
|
22
|
+
|
23
|
+
and optionally, a sensible initialize method. Let's look at each of these.
|
24
|
+
|
25
|
+
=== .initialize
|
26
|
+
|
27
|
+
Most custom wildcard matchers will want to define initialize to store
|
28
|
+
some information about just what should be matched. DivisibleBy#initialize
|
29
|
+
might look like this:
|
30
|
+
|
31
|
+
class RR::WildcardMatchers::DivisibleBy
|
32
|
+
def initialize(divisor)
|
33
|
+
@expected_divisor = divisor
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
=== #==(other)
|
38
|
+
DivisibleBy#==(other) should return true if other is a wildcard matcher that
|
39
|
+
matches the same things as self, so a natural way to write DivisibleBy#== is:
|
40
|
+
|
41
|
+
|
42
|
+
class RR::WildcardMatchers::DivisibleBy
|
43
|
+
def ==(other)
|
44
|
+
# Ensure that other is actually a DivisibleBy
|
45
|
+
return false unless other.is_a?(self.class)
|
46
|
+
|
47
|
+
# Does other expect to match the same divisor we do?
|
48
|
+
self.expected_divisor = other.expected_divisor
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Note that this implementation of #== assumes that we've also declared
|
53
|
+
attr_reader :expected_divisor
|
54
|
+
|
55
|
+
=== #inspect
|
56
|
+
|
57
|
+
Technically we don't have to declare DivisibleBy#inspect, since inspect is
|
58
|
+
defined for every object already. But putting a helpful message in inspect
|
59
|
+
will make test failures much clearer, and it only takes about two seconds to
|
60
|
+
write it, so let's be nice and do so:
|
61
|
+
|
62
|
+
class RR::WildcardMatchers::DivisibleBy
|
63
|
+
def inspect
|
64
|
+
"integer divisible by #{expected.divisor}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Now if we run the example from above:
|
69
|
+
|
70
|
+
mock(BananaGrabber).bunch_bananas(divisible_by(5))
|
71
|
+
|
72
|
+
and it fails, we get a helpful message saying
|
73
|
+
|
74
|
+
bunch_bananas(integer divisible by 5)
|
75
|
+
Called 0 times.
|
76
|
+
Expected 1 times.
|
77
|
+
|
78
|
+
=== #wildcard_matches?(other)
|
79
|
+
|
80
|
+
wildcard_matches? is the method that actually checks the argument against the
|
81
|
+
expectation. It should return true if other is considered to match,
|
82
|
+
false otherwise. In the case of DivisibleBy, wildcard_matches? reads:
|
83
|
+
|
84
|
+
class RR::WildcardMatchers::DivisibleBy
|
85
|
+
def wildcard_matches?(other)
|
86
|
+
# If other isn't a number, how can it be divisible by anything?
|
87
|
+
return false unless other.is_a?(Numeric)
|
88
|
+
|
89
|
+
# If other is in fact divisible by expected_divisor, then
|
90
|
+
# other modulo expected_divisor should be 0.
|
91
|
+
|
92
|
+
other % expected_divisor == 0
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
=== A finishing touch: wrapping it neatly
|
97
|
+
|
98
|
+
We could stop here if we were willing to resign ourselves to using
|
99
|
+
DivisibleBy this way:
|
100
|
+
|
101
|
+
mock(BananaGrabber).bunch_bananas(DivisibleBy.new(5))
|
102
|
+
|
103
|
+
But that's less expressive than the original:
|
104
|
+
|
105
|
+
mock(BananaGrabber).bunch_bananas(divisible_by(5))
|
106
|
+
|
107
|
+
To be able to use the convenient divisible_by matcher rather than the uglier
|
108
|
+
DivisibleBy.new version, re-open the module RR::Adapters::RRMethods and
|
109
|
+
define divisible_by there as a simple wrapper around DivisibleBy.new:
|
110
|
+
|
111
|
+
module RR::Adapters::RRMethods
|
112
|
+
def divisible_by(expected_divisor)
|
113
|
+
RR::WildcardMatchers::DivisibleBy.new(expected_divisor)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
== Recap
|
118
|
+
|
119
|
+
Here's all the code for DivisibleBy in one place for easy reference:
|
120
|
+
|
121
|
+
class RR::WildcardMatchers::DivisibleBy
|
122
|
+
def initialize(divisor)
|
123
|
+
@expected_divisor = divisor
|
124
|
+
end
|
125
|
+
|
126
|
+
def ==(other)
|
127
|
+
# Ensure that other is actually a DivisibleBy
|
128
|
+
return false unless other.is_a?(self.class)
|
129
|
+
|
130
|
+
# Does other expect to match the same divisor we do?
|
131
|
+
self.expected_divisor = other.expected_divisor
|
132
|
+
end
|
133
|
+
|
134
|
+
def inspect
|
135
|
+
"integer divisible by #{expected.divisor}"
|
136
|
+
end
|
137
|
+
|
138
|
+
def wildcard_matches?(other)
|
139
|
+
# If other isn't a number, how can it be divisible by anything?
|
140
|
+
return false unless other.is_a?(Numeric)
|
141
|
+
|
142
|
+
# If other is in fact divisible by expected_divisor, then
|
143
|
+
# other modulo expected_divisor should be 0.
|
144
|
+
|
145
|
+
other % expected_divisor == 0
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
module RR::Adapters::RRMethods
|
150
|
+
def divisible_by(expected_divisor)
|
151
|
+
RR::WildcardMatchers::DivisibleBy.new(expected_divisor)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
=end
|
156
|
+
|
157
|
+
module RR::WildcardMatchers
|
158
|
+
end
|
@@ -253,6 +253,26 @@ module RR
|
|
253
253
|
@result.should be
|
254
254
|
end
|
255
255
|
end
|
256
|
+
|
257
|
+
describe "that does not match" do
|
258
|
+
before do
|
259
|
+
@error = Object.new
|
260
|
+
@message = 'Verification error message'
|
261
|
+
stub(RR::Space.instance.recorded_calls).match_error { @error }
|
262
|
+
stub(@error).message { @message }
|
263
|
+
|
264
|
+
@matcher = InvocationMatcher.new(:foobar)
|
265
|
+
@result = @matcher.matches?(Object.new)
|
266
|
+
end
|
267
|
+
|
268
|
+
it "returns false when matching" do
|
269
|
+
@result.should_not be
|
270
|
+
end
|
271
|
+
|
272
|
+
it "returns a failure messsage" do
|
273
|
+
@matcher.failure_message.should == @message
|
274
|
+
end
|
275
|
+
end
|
256
276
|
end
|
257
277
|
end
|
258
278
|
end
|
@@ -15,6 +15,7 @@ class TestUnitBacktraceTest < Test::Unit::TestCase
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_backtrace_tweaking
|
18
|
+
skip "Skipped, because there is no Test::Unit::TestResult in this Test::Unit. Don't worry, this is totally fine." unless defined?(Test::Unit::TestResult)
|
18
19
|
old_result = @_result
|
19
20
|
result = Test::Unit::TestResult.new
|
20
21
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Takita
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-03-29 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -23,6 +23,7 @@ extra_rdoc_files:
|
|
23
23
|
- README.rdoc
|
24
24
|
- CHANGES
|
25
25
|
files:
|
26
|
+
- VERSION.yml
|
26
27
|
- README.rdoc
|
27
28
|
- Rakefile
|
28
29
|
- CHANGES
|
@@ -68,6 +69,7 @@ files:
|
|
68
69
|
- lib/rr/expectations/argument_equality_expectation.rb
|
69
70
|
- lib/rr/expectations/any_argument_expectation.rb
|
70
71
|
- lib/rr/expectations/times_called_expectation.rb
|
72
|
+
- lib/rr/wildcard_matchers.rb
|
71
73
|
- lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb
|
72
74
|
- lib/rr/double_definitions/strategies/implementation/reimplementation.rb
|
73
75
|
- lib/rr/double_definitions/strategies/implementation/proxy.rb
|
@@ -160,6 +162,8 @@ rdoc_options:
|
|
160
162
|
- README.rdoc
|
161
163
|
- --inline-source
|
162
164
|
- --line-numbers
|
165
|
+
- --inline-source
|
166
|
+
- --charset=UTF-8
|
163
167
|
require_paths:
|
164
168
|
- lib
|
165
169
|
required_ruby_version: !ruby/object:Gem::Requirement
|