rspec-expectations 2.9.1 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,8 +24,14 @@ module RSpec
24
24
  autoload :MatchArray, 'rspec/matchers/built_in/match_array'
25
25
  autoload :RaiseError, 'rspec/matchers/built_in/raise_error'
26
26
  autoload :RespondTo, 'rspec/matchers/built_in/respond_to'
27
+ autoload :StartWith, 'rspec/matchers/built_in/start_and_end_with'
28
+ autoload :EndWith, 'rspec/matchers/built_in/start_and_end_with'
27
29
  autoload :Satisfy, 'rspec/matchers/built_in/satisfy'
28
30
  autoload :ThrowSymbol, 'rspec/matchers/built_in/throw_symbol'
31
+ autoload :YieldControl, 'rspec/matchers/built_in/yield'
32
+ autoload :YieldWithArgs, 'rspec/matchers/built_in/yield'
33
+ autoload :YieldWithNoArgs, 'rspec/matchers/built_in/yield'
34
+ autoload :YieldSuccessiveArgs, 'rspec/matchers/built_in/yield'
29
35
  end
30
36
  end
31
37
  end
@@ -24,13 +24,14 @@ module RSpec
24
24
  @actual = actual
25
25
  end
26
26
 
27
- def match_unless_raises(exception=Exception)
27
+ def match_unless_raises(*exceptions)
28
+ exceptions.unshift Exception if exceptions.empty?
28
29
  begin
29
30
  yield
30
- true
31
- rescue exception => @rescued_exception
32
- false
31
+ rescue *exceptions => @rescued_exception
32
+ return false
33
33
  end
34
+ true
34
35
  end
35
36
 
36
37
  def failure_message_for_should
@@ -14,7 +14,7 @@ module RSpec
14
14
  unless defined?(@expected)
15
15
  raise ArgumentError.new("You must set an expected value using #of: be_within(#{delta}).of(expected_value)")
16
16
  end
17
- (super(actual) - expected).abs < delta
17
+ (super(actual) - expected).abs <= delta
18
18
  end
19
19
 
20
20
  def of(expected)
@@ -0,0 +1,50 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ class StartAndEndWith
5
+ include BaseMatcher
6
+
7
+ def initialize(*expected)
8
+ @expected = expected.length == 1 ? expected.first : expected
9
+ end
10
+
11
+ def matches?(actual)
12
+ @actual = actual.respond_to?(:[]) ? actual : (raise ArgumentError.new("#{actual.inspect} does not respond to :[]"))
13
+ begin
14
+ @expected.respond_to?(:length) ? subset_matches?(@expected, @actual) : element_matches?(@expected, @actual)
15
+ rescue ArgumentError
16
+ raise ArgumentError.new("#{actual.inspect} does not have ordered elements")
17
+ end
18
+ end
19
+
20
+ def failure_message_for_should
21
+ "expected #{@actual.inspect} to #{self.class.name.split('::').last.sub(/With/,'').downcase} with #{@expected.inspect}"
22
+ end
23
+
24
+ def failure_message_for_should_not
25
+ "expected #{@actual.inspect} not to #{self.class.name.split('::').last.sub(/With/,'').downcase} with #{@expected.inspect}"
26
+ end
27
+ end
28
+
29
+ class StartWith < StartAndEndWith
30
+ def subset_matches?(expected, actual)
31
+ actual[0, expected.length] == expected
32
+ end
33
+
34
+ def element_matches?(expected, actual)
35
+ @actual[0] == @expected
36
+ end
37
+ end
38
+
39
+ class EndWith < StartAndEndWith
40
+ def subset_matches?(expected, actual)
41
+ actual[-expected.length, expected.length] == expected
42
+ end
43
+
44
+ def element_matches?(expected, actual)
45
+ actual[-1] == expected
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,222 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ class YieldProbe
5
+ def self.probe(block)
6
+ probe = new
7
+ assert_valid_expect_block!(block)
8
+ block.call(probe)
9
+ probe.assert_used!
10
+ probe
11
+ end
12
+
13
+ attr_accessor :num_yields, :yielded_args
14
+
15
+ def initialize
16
+ @used = false
17
+ self.num_yields, self.yielded_args = 0, []
18
+ end
19
+
20
+ def to_proc
21
+ @used = true
22
+
23
+ probe = self
24
+ Proc.new do |*args|
25
+ probe.num_yields += 1
26
+ probe.yielded_args << args
27
+ end
28
+ end
29
+
30
+ def single_yield_args
31
+ yielded_args.first
32
+ end
33
+
34
+ def yielded_once?(matcher_name)
35
+ case num_yields
36
+ when 1 then true
37
+ when 0 then false
38
+ else
39
+ raise "The #{matcher_name} matcher is not designed to be used with a " +
40
+ "method that yields multiple times. Use the yield_successive_args " +
41
+ "matcher for that case."
42
+ end
43
+ end
44
+
45
+ def successive_yield_args
46
+ yielded_args.map do |arg_array|
47
+ arg_array.size == 1 ? arg_array.first : arg_array
48
+ end
49
+ end
50
+
51
+ def assert_used!
52
+ return if @used
53
+ raise "You must pass the argument yielded to your expect block on " +
54
+ "to the method-under-test as a block. It acts as a probe that " +
55
+ "allows the matcher to detect whether or not the method-under-test " +
56
+ "yields, and, if so, how many times, and what the yielded arguments " +
57
+ "are."
58
+ end
59
+
60
+ def self.assert_valid_expect_block!(block)
61
+ return if block.arity == 1
62
+ raise "Your expect block must accept an argument to be used with this " +
63
+ "matcher. Pass the argument as a block on to the method you are testing."
64
+ end
65
+ end
66
+
67
+ class YieldControl
68
+ include BaseMatcher
69
+
70
+ def matches?(block)
71
+ probe = YieldProbe.probe(block)
72
+ probe.yielded_once?(:yield_control)
73
+ end
74
+
75
+ def failure_message_for_should
76
+ "expected given block to yield control"
77
+ end
78
+
79
+ def failure_message_for_should_not
80
+ "expected given block not to yield control"
81
+ end
82
+ end
83
+
84
+ class YieldWithNoArgs
85
+ include BaseMatcher
86
+
87
+ def matches?(block)
88
+ @probe = YieldProbe.probe(block)
89
+ @probe.yielded_once?(:yield_with_no_args) && @probe.single_yield_args.none?
90
+ end
91
+
92
+ def failure_message_for_should
93
+ "expected given block to yield with no arguments, but #{failure_reason}"
94
+ end
95
+
96
+ def failure_message_for_should_not
97
+ "expected given block not to yield with no arguments, but did"
98
+ end
99
+
100
+ private
101
+
102
+ def failure_reason
103
+ if @probe.num_yields.zero?
104
+ "did not yield"
105
+ else
106
+ "yielded with arguments: #{@probe.single_yield_args.inspect}"
107
+ end
108
+ end
109
+ end
110
+
111
+ class YieldWithArgs
112
+ def initialize(*args)
113
+ @expected = args
114
+ end
115
+
116
+ def matches?(block)
117
+ @probe = YieldProbe.probe(block)
118
+ @actual = @probe.single_yield_args
119
+ @probe.yielded_once?(:yield_with_args) && args_match?
120
+ end
121
+
122
+ def failure_message_for_should
123
+ "expected given block to yield with arguments, but #{positive_failure_reason}"
124
+ end
125
+
126
+ def failure_message_for_should_not
127
+ "expected given block not to yield with arguments, but #{negative_failure_reason}"
128
+ end
129
+
130
+ def description
131
+ desc = "yield with args"
132
+ desc << "(" + @expected.map { |e| e.inspect }.join(", ") + ")" if @expected.any?
133
+ desc
134
+ end
135
+
136
+ private
137
+
138
+ def positive_failure_reason
139
+ if @probe.num_yields.zero?
140
+ "did not yield"
141
+ else
142
+ @positive_args_failure
143
+ end
144
+ end
145
+
146
+ def negative_failure_reason
147
+ if all_args_match?
148
+ "yielded with expected arguments" +
149
+ "\nexpected not: #{@expected.inspect}" +
150
+ "\n got: #{@actual.inspect} (compared using === and ==)"
151
+ else
152
+ "did"
153
+ end
154
+ end
155
+
156
+ def args_match?
157
+ if @expected.none? # expect {...}.to yield_with_args
158
+ @positive_args_failure = "yielded with no arguments" if @actual.none?
159
+ return @actual.any?
160
+ end
161
+
162
+ unless match = all_args_match?
163
+ @positive_args_failure = "yielded with unexpected arguments" +
164
+ "\nexpected: #{@expected.inspect}" +
165
+ "\n got: #{@actual.inspect} (compared using === and ==)"
166
+ end
167
+
168
+ match
169
+ end
170
+
171
+ def all_args_match?
172
+ return false if @expected.size != @actual.size
173
+
174
+ @expected.zip(@actual).all? do |expected, actual|
175
+ expected === actual || actual == expected
176
+ end
177
+ end
178
+ end
179
+
180
+ class YieldSuccessiveArgs
181
+ def initialize(*args)
182
+ @expected = args
183
+ end
184
+
185
+ def matches?(block)
186
+ @probe = YieldProbe.probe(block)
187
+ @actual = @probe.successive_yield_args
188
+ args_match?
189
+ end
190
+
191
+ def failure_message_for_should
192
+ "expected given block to yield successively with arguments, but yielded with unexpected arguments" +
193
+ "\nexpected: #{@expected.inspect}" +
194
+ "\n got: #{@actual.inspect} (compared using === and ==)"
195
+ end
196
+
197
+ def failure_message_for_should_not
198
+ "expected given block not to yield successively with arguments, but yielded with expected arguments" +
199
+ "\nexpected not: #{@expected.inspect}" +
200
+ "\n got: #{@actual.inspect} (compared using === and ==)"
201
+ end
202
+
203
+ def description
204
+ desc = "yield successive args"
205
+ desc << "(" + @expected.map { |e| e.inspect }.join(", ") + ")"
206
+ desc
207
+ end
208
+
209
+ private
210
+
211
+ def args_match?
212
+ return false if @expected.size != @actual.size
213
+
214
+ @expected.zip(@actual).all? do |expected, actual|
215
+ expected === actual || actual == expected
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+
@@ -7,7 +7,7 @@ module RSpec
7
7
  matcher_template = RSpec::Matchers::DSL::Matcher.new(name, &declarations)
8
8
  define_method name do |*expected|
9
9
  matcher = matcher_template.for_expected(*expected)
10
- matcher.matcher_execution_context = @matcher_execution_context || self
10
+ matcher.matcher_execution_context = @matcher_execution_context ||= self
11
11
  matcher
12
12
  end
13
13
  end
@@ -223,14 +223,14 @@ module RSpec
223
223
  end
224
224
 
225
225
  def respond_to?(method, include_private=false)
226
- super || @matcher_execution_context.respond_to?(method, include_private)
226
+ super || matcher_execution_context.respond_to?(method, include_private)
227
227
  end
228
228
 
229
229
  private
230
230
 
231
231
  def method_missing(method, *args, &block)
232
- if @matcher_execution_context.respond_to?(method)
233
- @matcher_execution_context.send method, *args, &block
232
+ if matcher_execution_context.respond_to?(method)
233
+ matcher_execution_context.send method, *args, &block
234
234
  else
235
235
  super(method, *args, &block)
236
236
  end
@@ -15,11 +15,17 @@ module RSpec::Matchers::BuiltIn
15
15
  matcher.match_unless_raises { raise }.should be_false
16
16
  end
17
17
 
18
- it "returns false if the submitted error is raised" do
18
+ it "returns false if the only submitted error is raised" do
19
19
  matcher.match_unless_raises(RuntimeError){ raise "foo" }.should be_false
20
20
  end
21
21
 
22
- it "re-raises any error other than the one specified" do
22
+ it "returns false if any of several errors submitted is raised" do
23
+ matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise "foo" }.should be_false
24
+ matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise ArgumentError.new('') }.should be_false
25
+ matcher.match_unless_raises(RuntimeError, ArgumentError, NameError) { raise NameError.new('') }.should be_false
26
+ end
27
+
28
+ it "re-raises any error other than one of those specified" do
23
29
  expect do
24
30
  matcher.match_unless_raises(ArgumentError){ raise "foo" }
25
31
  end.to raise_error
@@ -15,16 +15,16 @@ module RSpec
15
15
  be_within(0.5).of(5.0).matches?(4.51).should be_true
16
16
  end
17
17
 
18
- it "does not match when actual == (expected - delta)" do
19
- be_within(0.5).of(5.0).matches?(4.5).should be_false
18
+ it "matches when actual == (expected - delta)" do
19
+ be_within(0.5).of(5.0).matches?(4.5).should be_true
20
20
  end
21
21
 
22
22
  it "does not match when actual < (expected - delta)" do
23
23
  be_within(0.5).of(5.0).matches?(4.49).should be_false
24
24
  end
25
25
 
26
- it "does not match when actual == (expected + delta)" do
27
- be_within(0.5).of(5.0).matches?(5.5).should be_false
26
+ it "matches when actual == (expected + delta)" do
27
+ be_within(0.5).of(5.0).matches?(5.5).should be_true
28
28
  end
29
29
 
30
30
  it "does not match when actual > (expected + delta)" do
@@ -386,7 +386,7 @@ EOF
386
386
 
387
387
  describe "have(n).things on an object which is not a collection nor contains one" do
388
388
  it "fails" do
389
- lambda { Object.new.should have(2).things }.should raise_error(NoMethodError, /undefined method `things' for #<Object:/)
389
+ lambda { Object.new.should have(2).things }.should raise_error(NoMethodError) {|e| e.name.should eq :things }
390
390
  end
391
391
  end
392
392
 
@@ -43,8 +43,8 @@ describe "should_not raise_error" do
43
43
 
44
44
  it "fails if anything is raised" do
45
45
  lambda {
46
- lambda {raise}.should_not raise_error
47
- }.should fail_with("expected no Exception, got RuntimeError")
46
+ lambda { raise RuntimeError, "example message" }.should_not raise_error
47
+ }.should fail_with("expected no Exception, got #<RuntimeError: example message>")
48
48
  end
49
49
  end
50
50
 
@@ -102,8 +102,8 @@ describe "should raise_error(NamedError)" do
102
102
 
103
103
  it "fails if another error is raised (NameError)" do
104
104
  lambda {
105
- lambda { raise }.should raise_error(NameError)
106
- }.should fail_with("expected NameError, got RuntimeError")
105
+ lambda { raise RuntimeError, "example message" }.should raise_error(NameError)
106
+ }.should fail_with("expected NameError, got #<RuntimeError: example message>")
107
107
  end
108
108
 
109
109
  it "fails if another error is raised (NameError)" do
@@ -142,8 +142,8 @@ describe "should raise_error(NamedError, error_message) with String" do
142
142
 
143
143
  it "fails if incorrect error is raised" do
144
144
  lambda {
145
- lambda { raise }.should raise_error(NameError, "example message")
146
- }.should fail_with("expected NameError with \"example message\", got RuntimeError")
145
+ lambda { raise RuntimeError, "example message" }.should raise_error(NameError, "example message")
146
+ }.should fail_with("expected NameError with \"example message\", got #<RuntimeError: example message>")
147
147
  end
148
148
 
149
149
  it "fails if correct error is raised with incorrect message" do
@@ -307,8 +307,8 @@ describe "should raise_error(NamedError, error_message) with Regexp" do
307
307
 
308
308
  it "fails if incorrect error is raised" do
309
309
  lambda {
310
- lambda { raise }.should raise_error(NameError, /ample mess/)
311
- }.should fail_with("expected NameError with message matching /ample mess/, got RuntimeError")
310
+ lambda { raise RuntimeError, "example message" }.should raise_error(NameError, /ample mess/)
311
+ }.should fail_with("expected NameError with message matching /ample mess/, got #<RuntimeError: example message>")
312
312
  end
313
313
 
314
314
  it "fails if correct error is raised with incorrect message" do
@@ -0,0 +1,174 @@
1
+ require "spec_helper"
2
+
3
+ describe "should start_with" do
4
+ context "with a string" do
5
+ it "passes if it matches the start of the actual string" do
6
+ "this string".should start_with "this str"
7
+ end
8
+
9
+ it "fails if it does not match the start of the actual string" do
10
+ expect {
11
+ "this string".should start_with "that str"
12
+ }.to fail_with("expected \"this string\" to start with \"that str\"")
13
+ end
14
+ end
15
+
16
+ context "with an array" do
17
+ it "passes if it is the first element of the array" do
18
+ [0, 1, 2].should start_with 0
19
+ end
20
+
21
+ it "passes if the first elements of the array match" do
22
+ [0, 1, 2].should start_with 0, 1
23
+ end
24
+
25
+ it "fails if it does not match the first element of the array" do
26
+ expect {
27
+ [0, 1, 2].should start_with 2
28
+ }.to fail_with("expected [0, 1, 2] to start with 2")
29
+ end
30
+
31
+ it "fails if it the first elements of the array do not match" do
32
+ expect {
33
+ [0, 1, 2].should start_with 1, 2
34
+ }.to fail_with("expected [0, 1, 2] to start with [1, 2]")
35
+ end
36
+ end
37
+
38
+ context "with an object that does not respond to :[]" do
39
+ it "raises an ArgumentError" do
40
+ expect { Object.new.should start_with 0 }.to raise_error(ArgumentError, /does not respond to :\[\]/)
41
+ end
42
+ end
43
+
44
+ context "with a hash" do
45
+ it "raises an ArgumentError if trying to match more than one element" do
46
+ expect{
47
+ {:a => 'b', :b => 'b', :c => 'c'}.should start_with({:a => 'b', :b => 'b'})
48
+ }.to raise_error(ArgumentError, /does not have ordered elements/)
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "should_not start_with" do
54
+ context "with a string" do
55
+ it "passes if it does not match the start of the actual string" do
56
+ "this string".should_not start_with "that str"
57
+ end
58
+
59
+ it "fails if it does match the start of the actual string" do
60
+ expect {
61
+ "this string".should_not start_with "this str"
62
+ }.to fail_with("expected \"this string\" not to start with \"this str\"")
63
+ end
64
+ end
65
+
66
+ context "with an array" do
67
+ it "passes if it is not the first element of the array" do
68
+ [0, 1, 2].should_not start_with 2
69
+ end
70
+
71
+ it "passes if the first elements of the array do not match" do
72
+ [0, 1, 2].should_not start_with 1, 2
73
+ end
74
+
75
+ it "fails if it matches the first element of the array" do
76
+ expect {
77
+ [0, 1, 2].should_not start_with 0
78
+ }.to fail_with("expected [0, 1, 2] not to start with 0")
79
+ end
80
+
81
+ it "fails if it the first elements of the array match" do
82
+ expect {
83
+ [0, 1, 2].should_not start_with 0, 1
84
+ }.to fail_with("expected [0, 1, 2] not to start with [0, 1]")
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "should end_with" do
90
+ context "with a string" do
91
+ it "passes if it matches the end of the actual string" do
92
+ "this string".should end_with "is string"
93
+ end
94
+
95
+ it "fails if it does not match the end of the actual string" do
96
+ expect {
97
+ "this string".should end_with "is stringy"
98
+ }.to fail_with("expected \"this string\" to end with \"is stringy\"")
99
+ end
100
+ end
101
+
102
+ context "with an array" do
103
+ it "passes if it is the last element of the array" do
104
+ [0, 1, 2].should end_with 2
105
+ end
106
+
107
+ it "passes if the last elements of the array match" do
108
+ [0, 1, 2].should end_with [1, 2]
109
+ end
110
+
111
+ it "fails if it does not match the last element of the array" do
112
+ expect {
113
+ [0, 1, 2].should end_with 1
114
+ }.to fail_with("expected [0, 1, 2] to end with 1")
115
+ end
116
+
117
+ it "fails if it the last elements of the array do not match" do
118
+ expect {
119
+ [0, 1, 2].should end_with [0, 1]
120
+ }.to fail_with("expected [0, 1, 2] to end with [0, 1]")
121
+ end
122
+ end
123
+
124
+ context "with an object that does not respond to :[]" do
125
+ it "should raise an error if expected value can't be indexed'" do
126
+ expect { Object.new.should end_with 0 }.to raise_error(ArgumentError, /does not respond to :\[\]/)
127
+ end
128
+ end
129
+
130
+ context "with a hash" do
131
+ it "raises an ArgumentError if trying to match more than one element" do
132
+ expect{
133
+ {:a => 'b', :b => 'b', :c => 'c'}.should end_with({:a => 'b', :b =>'b'})
134
+ }.to raise_error(ArgumentError, /does not have ordered elements/)
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ describe "should_not end_with" do
141
+ context "with a sting" do
142
+ it "passes if it does not match the end of the actual string" do
143
+ "this string".should_not end_with "stringy"
144
+ end
145
+
146
+ it "fails if it matches the end of the actual string" do
147
+ expect {
148
+ "this string".should_not end_with "string"
149
+ }.to fail_with("expected \"this string\" not to end with \"string\"")
150
+ end
151
+ end
152
+
153
+ context "an array" do
154
+ it "passes if it is not the last element of the array" do
155
+ [0, 1, 2].should_not end_with 1
156
+ end
157
+
158
+ it "passes if the last elements of the array do not match" do
159
+ [0, 1, 2].should_not end_with [0, 1]
160
+ end
161
+
162
+ it "fails if it matches the last element of the array" do
163
+ expect {
164
+ [0, 1, 2].should_not end_with 2
165
+ }.to fail_with("expected [0, 1, 2] not to end with 2")
166
+ end
167
+
168
+ it "fails if it the last elements of the array match" do
169
+ expect {
170
+ [0, 1, 2].should_not end_with [1, 2]
171
+ }.to fail_with("expected [0, 1, 2] not to end with [1, 2]")
172
+ end
173
+ end
174
+ end