rspec_multi_matchers 1.0.8 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +77 -0
- data/lib/Gemfile.lock +17 -0
- data/lib/match_each.rb +78 -0
- data/lib/match_enum.rb +75 -0
- data/lib/match_in_order.rb +31 -0
- data/lib/rspec_multi_matchers.rb +10 -0
- data/spec/lib/match_each_spec.rb +23 -0
- data/spec/lib/match_enum_spec.rb +23 -0
- data/spec/lib/match_in_order_spec.rb +30 -0
- data/spec/shared_enum_spec.rb +30 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +4 -0
- metadata +23 -7
data/README.rdoc
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
= rspec-multi-matchers
|
2
|
+
|
3
|
+
== Summary
|
4
|
+
* test collection using each or other enumerable methods
|
5
|
+
* tests fell more natural and have a friendlier failure message
|
6
|
+
|
7
|
+
== HomePage
|
8
|
+
* http://github.com/gregwebs/rspec-multi-matchers
|
9
|
+
|
10
|
+
== DESCRIPTION:
|
11
|
+
|
12
|
+
* match_each
|
13
|
+
* match_enum
|
14
|
+
* match_in_order
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
require 'spec'
|
18
|
+
require 'rspec_multi_matchers'
|
19
|
+
|
20
|
+
describe 'array of ones' do
|
21
|
+
it 'should be all ones' do
|
22
|
+
[1,2,3].should each { |n|
|
23
|
+
n.should == 1
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
=begin output
|
29
|
+
'array of ones should fail on 2' FAILED
|
30
|
+
line: 14
|
31
|
+
item 1: 2
|
32
|
+
expected: 1,
|
33
|
+
got: 2 (using ==)
|
34
|
+
=end
|
35
|
+
|
36
|
+
As expected, the output shows expected and got fields
|
37
|
+
line is the line number of the expectiation inside the block
|
38
|
+
the item line gives the index of the item being yielded to the block, and the item itself
|
39
|
+
|
40
|
+
|
41
|
+
=== Warning
|
42
|
+
|
43
|
+
Note the use of brackets '{ ... }' instead of 'do ... end'
|
44
|
+
this is necessary because 'do .. end' does not bind strong enough
|
45
|
+
|
46
|
+
== RELATED ARTICLES:
|
47
|
+
|
48
|
+
* http://blog.thoughtfolder.com/2008-11-05-rspec-should-each-matcher.html
|
49
|
+
|
50
|
+
== INSTALL:
|
51
|
+
|
52
|
+
* gem install rspec_multi_matchers
|
53
|
+
|
54
|
+
== LICENSE:
|
55
|
+
|
56
|
+
(The MIT License)
|
57
|
+
|
58
|
+
Copyright (c) 2008 Greg Weber
|
59
|
+
|
60
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
61
|
+
a copy of this software and associated documentation files (the
|
62
|
+
'Software'), to deal in the Software without restriction, including
|
63
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
64
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
65
|
+
permit persons to whom the Software is furnished to do so, subject to
|
66
|
+
the following conditions:
|
67
|
+
|
68
|
+
The above copyright notice and this permission notice shall be
|
69
|
+
included in all copies or substantial portions of the Software.
|
70
|
+
|
71
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
72
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
73
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
74
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
75
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
76
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
77
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/Gemfile.lock
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
GEM
|
2
|
+
specs:
|
3
|
+
columnize (0.3.2)
|
4
|
+
linecache (0.43)
|
5
|
+
rspec (1.3.1)
|
6
|
+
ruby-debug (0.10.4)
|
7
|
+
columnize (>= 0.1)
|
8
|
+
ruby-debug-base (~> 0.10.4.0)
|
9
|
+
ruby-debug-base (0.10.4)
|
10
|
+
linecache (>= 0.3)
|
11
|
+
|
12
|
+
PLATFORMS
|
13
|
+
ruby
|
14
|
+
|
15
|
+
DEPENDENCIES
|
16
|
+
rspec (< 2)
|
17
|
+
ruby-debug
|
data/lib/match_each.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
class MatchEach
|
2
|
+
|
3
|
+
class MatchEachError < Exception; end
|
4
|
+
class NoBlockGivenError < MatchEachError; end
|
5
|
+
|
6
|
+
class BlankEnumerableError < MatchEachError; end
|
7
|
+
|
8
|
+
def initialize(options, &block)
|
9
|
+
@empty_okay = (options and options[:empty])
|
10
|
+
unless @block = block
|
11
|
+
raise NoBlockGivenError, 'No block given. You probably need to use brackets "{...}" instead of "do...end"'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def matches?(target)
|
16
|
+
if target.nil?
|
17
|
+
raise BlankEnumerableError, "Expected an enumerable object, but got nil"
|
18
|
+
end
|
19
|
+
|
20
|
+
if !@empty_okay && target.empty?
|
21
|
+
raise BlankEnumerableError, "No items in the given enumerator.\nTo allow an empty enumerator pass the :empty option with a true value"
|
22
|
+
end
|
23
|
+
|
24
|
+
@counter = 0
|
25
|
+
target.each do |obj|
|
26
|
+
begin
|
27
|
+
@block.call(obj)
|
28
|
+
rescue Spec::Expectations::ExpectationNotMetError => e
|
29
|
+
@error = e
|
30
|
+
@failure_object = obj
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
@counter += 1
|
34
|
+
end
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def object_description
|
39
|
+
insp = @failure_object.inspect
|
40
|
+
return insp if insp.length < 300
|
41
|
+
|
42
|
+
if @failure_object.respond_to?(:to_s)
|
43
|
+
str = @failure_object.to_s
|
44
|
+
return str if str .length < 300
|
45
|
+
end
|
46
|
+
|
47
|
+
insp[0..300] + ' ... '
|
48
|
+
end
|
49
|
+
|
50
|
+
def failure_line
|
51
|
+
# find 'matches?' in statck trace
|
52
|
+
# then move back to the first line number that is not a function call
|
53
|
+
error_line = nil
|
54
|
+
@error.backtrace.each do |line|
|
55
|
+
if line.match(/:\d+:in\s*[`'"](.*)[`'"]\s*$/)
|
56
|
+
return error_line if $1 == 'matches?'
|
57
|
+
else
|
58
|
+
error_line = line.match(/^[^:]+:(\d+)/)[1]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
nil # should not reach here
|
63
|
+
end
|
64
|
+
|
65
|
+
def failure_message
|
66
|
+
padding = ' ' * if @error.message =~ /expected not/ then 4 else 0 end
|
67
|
+
|
68
|
+
[" line: #{failure_line}",
|
69
|
+
" item #{@counter}: #{object_description}"
|
70
|
+
].map { |line| padding + line }.push(@error.message).join("\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
# no need for should_not, so no negative_failure_messages
|
74
|
+
end
|
75
|
+
|
76
|
+
def each(options=nil, &block)
|
77
|
+
MatchEach.new options, &block
|
78
|
+
end
|
data/lib/match_enum.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
class MatchEnum
|
2
|
+
@@enum_methods = [] # avoid re-defining
|
3
|
+
|
4
|
+
class MatchEnumError < Exception; end
|
5
|
+
class BlankEnumerableError < MatchEnumError; end
|
6
|
+
class NoBlockGivenError < MatchEnumError; end
|
7
|
+
|
8
|
+
def initialize(method, *enum_args, &block)
|
9
|
+
options = enum_args.pop if Hash === enum_args.last
|
10
|
+
@method = method
|
11
|
+
@enum_args = enum_args
|
12
|
+
@empty_okay = (options and options[:empty])
|
13
|
+
@block = block
|
14
|
+
if !@block
|
15
|
+
raise NoBlockGivenError, 'no block given, you probably need to use brackets instead of "do...end"'
|
16
|
+
end
|
17
|
+
|
18
|
+
@num_block_args = @block.arity
|
19
|
+
@num_block_args = 0 if @num_block_args == -1 # correct ruby error
|
20
|
+
if enum_args.empty?
|
21
|
+
return if @@enum_methods[@num_block_args]
|
22
|
+
@@enum_methods[@num_block_args] = true
|
23
|
+
end
|
24
|
+
|
25
|
+
args = (1..(@num_block_args)).map {|i| 'arg_' << i.to_s}.join(',')
|
26
|
+
invoke_enum = if @enum_args.empty?
|
27
|
+
"target.send(@method) do |#{args}|"
|
28
|
+
else
|
29
|
+
"target.send(@method, *@enum_args) do |#{args}|"
|
30
|
+
end
|
31
|
+
eval <<-EOS
|
32
|
+
def enum_#{@num_block_args}(target)
|
33
|
+
@counter = 0
|
34
|
+
#{invoke_enum}
|
35
|
+
begin
|
36
|
+
@block.call(#{args})
|
37
|
+
rescue Spec::Expectations::ExpectationNotMetError => e
|
38
|
+
@error_msg = e.to_s
|
39
|
+
@failure_object = [#{args}]
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
@counter += 1
|
43
|
+
end
|
44
|
+
true
|
45
|
+
end
|
46
|
+
EOS
|
47
|
+
end
|
48
|
+
|
49
|
+
def enum(target)
|
50
|
+
eval("enum_#{@num_block_args}(target)")
|
51
|
+
end
|
52
|
+
|
53
|
+
def matches?(target)
|
54
|
+
if target.nil?
|
55
|
+
raise BlankEnumerableError, "Expected an enumerable object, but got nil"
|
56
|
+
end
|
57
|
+
|
58
|
+
if !@empty_okay && target.empty?
|
59
|
+
raise BlankEnumerableError, "No items in the given enumerator.\nTo allow an empty enumerator pass the :empty option with a true value"
|
60
|
+
end
|
61
|
+
|
62
|
+
return enum(target)
|
63
|
+
end
|
64
|
+
|
65
|
+
def failure_message
|
66
|
+
if @error_msg =~ /expected not/ then ' ' else '' end <<
|
67
|
+
" item #{@counter}: #{@failure_object.inspect}\n#{@error_msg}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# no need for should_not, so no negative_failure_messages
|
71
|
+
end
|
72
|
+
|
73
|
+
def enum(method, *args, &block)
|
74
|
+
MatchEnum.new method, *args, &block
|
75
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class MatchInOrder
|
2
|
+
def initialize(regexps)
|
3
|
+
@regexps = regexps
|
4
|
+
end
|
5
|
+
|
6
|
+
def matches?(target)
|
7
|
+
@target = target
|
8
|
+
@regexps.inject(target) do |str, regexp|
|
9
|
+
m = str.match(regexp)
|
10
|
+
if m.nil?
|
11
|
+
@failure_string = str
|
12
|
+
@failure_regex = regexp
|
13
|
+
return false
|
14
|
+
end
|
15
|
+
m.post_match
|
16
|
+
end
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure_message
|
21
|
+
"expected #{@failure_string.inspect} to match #{@failure_regex.inspect}\nwithin string: #{@target.inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def negative_failure_message
|
25
|
+
"expected #{@target.inspect} to not match in order against: #{@regexps.inspect}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def match_in_order(*regexps)
|
30
|
+
MatchInOrder.new(regexps)
|
31
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
module RspecMultiMatchers
|
5
|
+
VERSION = '1.1.0'
|
6
|
+
end
|
7
|
+
|
8
|
+
require "match_each"
|
9
|
+
require "match_enum"
|
10
|
+
require "match_in_order"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe MatchEach do
|
4
|
+
it_should_behave_like "each matcher"
|
5
|
+
|
6
|
+
before do
|
7
|
+
@class = MatchEach
|
8
|
+
@iterator = [:each]
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should add to the inner error message' do
|
12
|
+
begin
|
13
|
+
2.should == 1
|
14
|
+
rescue Rspec::Expectations::ExpectationNotMetError => e
|
15
|
+
@line = __LINE__ + 2
|
16
|
+
lambda{ [1,2,3].should each { |n|
|
17
|
+
n.should == 1
|
18
|
+
} }.should raise_error(
|
19
|
+
Rspec::Expectations::ExpectationNotMetError, /^\s*line: #{@line}\s*item 1: 2\s*#{Regexp.escape(e.message)}/m)
|
20
|
+
else fail
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe MatchEnum do
|
4
|
+
it_should_behave_like "each matcher"
|
5
|
+
|
6
|
+
before do
|
7
|
+
@class = MatchEnum
|
8
|
+
@iterator = [:enum, :each]
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should enum with each_with_index' do
|
12
|
+
[1,2,3].should enum(:each_with_index) { |n,i| n.should == i + 1 }
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should enum with each_slice' do
|
16
|
+
acc = []
|
17
|
+
[1,2,3].should enum(:each_cons, 2) { |(x,y)|
|
18
|
+
acc << [x,y]
|
19
|
+
x.should_not == y
|
20
|
+
}
|
21
|
+
acc.should == [[1,2],[2,3]]
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe MatchInOrder do
|
4
|
+
|
5
|
+
it "should match a single regular expression" do
|
6
|
+
"a".should match_in_order(//)
|
7
|
+
"a".should match_in_order(/a/)
|
8
|
+
"a".should_not match_in_order(/b/)
|
9
|
+
"z".should_not match_in_order(/a/)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not match the same regular expressions twice" do
|
13
|
+
"a".should_not match_in_order(/a/,/a/)
|
14
|
+
lambda{ "a".should match_in_order(/a/,/a/) }.should raise_error(Rspec::Expectations::ExpectationNotMetError, 'expected "" to match /a/' << "\n" << 'within string: "a"')
|
15
|
+
|
16
|
+
"abc".should_not match_in_order(/a/,/b/,/c/,/a/)
|
17
|
+
lambda{ "abc".should match_in_order(/a/,/b/,/c/,/a/) }.should raise_error(Rspec::Expectations::ExpectationNotMetError, 'expected "" to match /a/' << "\n" << 'within string: "abc"')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should match multiple regular expressions in order" do
|
21
|
+
"abc".should match_in_order(/a/,/b/,/c/)
|
22
|
+
lambda{ "abc".should_not match_in_order(/a/,/b/,/c/) }.should raise_error(Rspec::Expectations::ExpectationNotMetError,'expected "abc" to not match in order against: [/a/, /b/, /c/]')
|
23
|
+
|
24
|
+
"abc".should_not match_in_order(/a/,/c/,/b/)
|
25
|
+
lambda{ "abc".should match_in_order(/a/,/c/,/b/) }.should raise_error(Rspec::Expectations::ExpectationNotMetError,'expected "" to match /b/' << "\n" << 'within string: "abc"')
|
26
|
+
|
27
|
+
"abc".should_not match_in_order(/b/,/a/,/c/)
|
28
|
+
lambda{ "abc".should match_in_order(/b/,/a/,/c/) }.should raise_error(Rspec::Expectations::ExpectationNotMetError,'expected "c" to match /a/' << "\n" << 'within string: "abc"')
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
shared_examples_for "each matcher" do
|
2
|
+
it 'should raise an error when a block is not given' do
|
3
|
+
# do...end does not bind strongly enough, must use brackets
|
4
|
+
lambda do
|
5
|
+
[1,2,3].should send(*@iterator) do |n| n.should == n end
|
6
|
+
end.should raise_error(@class::NoBlockGivenError)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should not raise an error for met expectations' do
|
10
|
+
i = 0
|
11
|
+
[1,2,3].should send(*@iterator) {|n| n.should == (i+=1)}
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should raise an error if there are no items in the enumerable object' do
|
15
|
+
lambda{ [].should send(*@iterator){} }.should raise_error(@class::BlankEnumerableError)
|
16
|
+
[false, nil].each do |f|
|
17
|
+
@iterator.push(:empty => f)
|
18
|
+
lambda{ [].should send(*@iterator){}}.should raise_error(@class::BlankEnumerableError)
|
19
|
+
@iterator.pop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should not raise an error if there are no items in the enumerable object and the empty flag is passed' do
|
24
|
+
['okay', true].each do |t|
|
25
|
+
@iterator.push(:empty => t)
|
26
|
+
lambda{ [].should send(*@iterator){} }.should_not raise_error
|
27
|
+
@iterator.pop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec_multi_matchers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 1.0.8
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Greg Weber
|
@@ -27,8 +27,19 @@ extensions: []
|
|
27
27
|
|
28
28
|
extra_rdoc_files: []
|
29
29
|
|
30
|
-
files:
|
31
|
-
|
30
|
+
files:
|
31
|
+
- lib/Gemfile.lock
|
32
|
+
- lib/match_in_order.rb
|
33
|
+
- lib/match_each.rb
|
34
|
+
- lib/rspec_multi_matchers.rb
|
35
|
+
- lib/match_enum.rb
|
36
|
+
- README.rdoc
|
37
|
+
- spec/spec_helper.rb
|
38
|
+
- spec/lib/match_in_order_spec.rb
|
39
|
+
- spec/lib/match_each_spec.rb
|
40
|
+
- spec/lib/match_enum_spec.rb
|
41
|
+
- spec/shared_enum_spec.rb
|
42
|
+
- spec/spec.opts
|
32
43
|
has_rdoc: true
|
33
44
|
homepage:
|
34
45
|
licenses: []
|
@@ -63,5 +74,10 @@ rubygems_version: 1.6.2
|
|
63
74
|
signing_key:
|
64
75
|
specification_version: 3
|
65
76
|
summary: better collection testing
|
66
|
-
test_files:
|
67
|
-
|
77
|
+
test_files:
|
78
|
+
- spec/spec_helper.rb
|
79
|
+
- spec/lib/match_in_order_spec.rb
|
80
|
+
- spec/lib/match_each_spec.rb
|
81
|
+
- spec/lib/match_enum_spec.rb
|
82
|
+
- spec/shared_enum_spec.rb
|
83
|
+
- spec/spec.opts
|