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.
@@ -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.
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,4 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+ $:.unshift(File.dirname(__FILE__))
3
+ require 'rspec_multi_matchers'
4
+ require 'shared_enum_spec'
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: 7
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 8
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