rspec-expectations 2.9.1 → 2.10.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.
@@ -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