dchelimsky-rspec 1.1.11.1 → 1.1.11.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -3
- data/Manifest.txt +35 -46
- data/README.txt +30 -12
- data/Rakefile +9 -9
- data/{stories/configuration/before_blocks.story → features/configuration/before_blocks.feature} +2 -2
- data/{stories/example_groups/autogenerated_docstrings → features/example_groups/autogenerated_docstrings.feature} +1 -1
- data/{stories/example_groups/example_group_with_should_methods → features/example_groups/example_group_with_should_methods.feature} +3 -3
- data/{stories/example_groups/nested_groups → features/example_groups/nested_groups.feature} +1 -1
- data/{stories/example_groups/output → features/example_groups/output.feature} +3 -8
- data/{stories/interop/examples_and_tests_together → features/interop/examples_and_tests_together.feature} +5 -4
- data/{stories/interop/test_but_not_test_unit → features/interop/test_but_not_test_unit.feature} +2 -2
- data/{stories/interop/test_case_with_should_methods → features/interop/test_case_with_should_methods.feature} +2 -2
- data/{stories/mock_framework_integration/use_flexmock.story → features/mock_framework_integration/use_flexmock.feature} +1 -1
- data/features/step_definitions/running_rspec.rb +48 -0
- data/features/support/env.rb +25 -0
- data/{stories/resources → features/support}/helpers/cmdline.rb +0 -0
- data/{stories/resources → features/support}/helpers/story_helper.rb +0 -0
- data/{stories/resources → features/support}/matchers/smart_match.rb +0 -0
- data/{plugins → lib/adapters}/mock_frameworks/flexmock.rb +0 -0
- data/{plugins → lib/adapters}/mock_frameworks/mocha.rb +0 -0
- data/{plugins → lib/adapters}/mock_frameworks/rr.rb +0 -0
- data/{plugins → lib/adapters}/mock_frameworks/rspec.rb +1 -1
- data/lib/autotest/rspec.rb +3 -2
- data/lib/spec/dsl/main.rb +8 -4
- data/lib/spec/example/configuration.rb +1 -1
- data/lib/spec/example/example_group_factory.rb +6 -2
- data/lib/spec/example/example_group_methods.rb +47 -54
- data/lib/spec/example/shared_example_group.rb +2 -2
- data/lib/spec/example.rb +163 -14
- data/lib/spec/interop/test/unit/testresult.rb +1 -1
- data/lib/spec/interop/test/unit/testsuite_adapter.rb +1 -1
- data/lib/spec/matchers/match_array.rb +75 -0
- data/lib/spec/matchers/operator_matcher.rb +34 -7
- data/lib/spec/matchers.rb +2 -1
- data/lib/spec/mocks/argument_constraints.rb +43 -5
- data/lib/spec/runner/example_group_runner.rb +2 -2
- data/lib/spec/runner/option_parser.rb +12 -6
- data/lib/spec/runner/options.rb +9 -9
- data/lib/spec/runner/spec_parser.rb +3 -2
- data/lib/spec/runner.rb +54 -188
- data/lib/spec/version.rb +1 -1
- data/lib/spec.rb +0 -29
- data/{rake_tasks → resources/rake}/examples.rake +0 -0
- data/{rake_tasks → resources/rake}/examples_with_rcov.rake +0 -0
- data/{rake_tasks → resources/rake}/failing_examples_with_html.rake +0 -0
- data/{rake_tasks → resources/rake}/verify_rcov.rake +0 -0
- data/{stories/resources → resources}/spec/before_blocks_example.rb +1 -1
- data/{stories/resources → resources}/spec/example_group_with_should_methods.rb +1 -1
- data/{stories/resources → resources}/spec/simple_spec.rb +1 -1
- data/resources/spec/spec_with_flexmock.rb +19 -0
- data/{stories/resources → resources}/test/spec_and_test_together.rb +1 -1
- data/{stories/resources → resources}/test/spec_including_test_but_not_unit.rb +1 -1
- data/{stories/resources → resources}/test/test_case_with_should_methods.rb +2 -2
- data/rspec.gemspec +5 -4
- data/spec/autotest/rspec_spec.rb +2 -1
- data/spec/spec/dsl/main_spec.rb +8 -5
- data/spec/spec/example/configuration_spec.rb +9 -9
- data/spec/spec/example/example_group_factory_spec.rb +22 -3
- data/spec/spec/example/example_group_methods_spec.rb +19 -15
- data/spec/spec/example/example_group_spec.rb +41 -41
- data/spec/spec/example/example_methods_spec.rb +5 -5
- data/spec/spec/example/helper_method_spec.rb +24 -0
- data/spec/spec/example/pending_module_spec.rb +2 -8
- data/spec/spec/example/shared_example_group_spec.rb +5 -5
- data/spec/spec/matchers/description_generation_spec.rb +5 -0
- data/spec/spec/matchers/handler_spec.rb +7 -7
- data/spec/spec/matchers/match_array_spec.rb +83 -0
- data/spec/spec/matchers/raise_error_spec.rb +18 -0
- data/spec/spec/mocks/hash_including_matcher_spec.rb +39 -2
- data/spec/spec/mocks/hash_not_including_matcher_spec.rb +67 -0
- data/spec/spec/mocks/mock_spec.rb +5 -6
- data/spec/spec/mocks/nil_expectation_warning_spec.rb +1 -1
- data/spec/spec/runner/formatter/base_text_formatter_spec.rb +22 -0
- data/spec/spec/runner/option_parser_spec.rb +4 -17
- data/spec/spec/runner/options_spec.rb +19 -5
- data/spec/spec/runner/resources/custom_example_group_runner.rb +14 -0
- data/spec/spec/runner/spec_parser_spec.rb +10 -0
- data/spec/spec/spec_spec.rb +21 -0
- metadata +39 -49
- data/lib/spec/adapters/ruby_engine/mri.rb +0 -8
- data/lib/spec/adapters/ruby_engine/rubinius.rb +0 -8
- data/lib/spec/adapters/ruby_engine.rb +0 -26
- data/lib/spec/adapters.rb +0 -1
- data/lib/spec/extensions/class.rb +0 -24
- data/lib/spec/extensions.rb +0 -1
- data/spec/spec/adapters/ruby_engine_spec.rb +0 -16
- data/stories/all.rb +0 -5
- data/stories/configuration/stories.rb +0 -7
- data/stories/example_groups/stories.rb +0 -7
- data/stories/helper.rb +0 -6
- data/stories/interop/stories.rb +0 -7
- data/stories/mock_framework_integration/stories.rb +0 -7
- data/stories/pending_stories/README +0 -3
- data/stories/resources/spec/spec_with_flexmock.rb +0 -18
- data/stories/resources/steps/running_rspec.rb +0 -50
- data/stories/resources/stories/failing_story.rb +0 -15
- data/stories/stories/multiline_steps.story +0 -23
- data/stories/stories/steps/multiline_steps.rb +0 -13
- data/stories/stories/stories.rb +0 -6
@@ -13,13 +13,13 @@ module Spec
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.description_text(*args)
|
16
|
-
args.inject("") do |
|
17
|
-
|
18
|
-
|
16
|
+
args.inject("") do |description, arg|
|
17
|
+
description << " " unless (description == "" || arg.to_s =~ /^(\s|\.|#)/)
|
18
|
+
description << arg.to_s
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
attr_reader :
|
22
|
+
attr_reader :description_options, :spec_path
|
23
23
|
alias :options :description_options
|
24
24
|
|
25
25
|
# Provides the backtrace up to where this example_group was declared.
|
@@ -42,7 +42,7 @@ WARNING
|
|
42
42
|
|
43
43
|
def inherited(klass)
|
44
44
|
super
|
45
|
-
klass
|
45
|
+
Spec::Runner.options.add_example_group klass
|
46
46
|
Spec::Runner.register_at_exit_hook
|
47
47
|
end
|
48
48
|
|
@@ -61,11 +61,9 @@ WARNING
|
|
61
61
|
# end
|
62
62
|
#
|
63
63
|
def describe(*args, &example_group_block)
|
64
|
-
args << {} unless Hash === args.last
|
65
64
|
if example_group_block
|
65
|
+
Spec::Example::add_spec_path_to(args)
|
66
66
|
options = args.last
|
67
|
-
# Ruby 1.9 - the next line uses example_group_block.binding instead of example_group_block
|
68
|
-
options[:spec_path] = eval("caller(0)[1]", example_group_block.binding) unless options[:spec_path]
|
69
67
|
if options[:shared]
|
70
68
|
create_shared_example_group(*args, &example_group_block)
|
71
69
|
else
|
@@ -82,12 +80,28 @@ WARNING
|
|
82
80
|
end
|
83
81
|
|
84
82
|
def create_subclass(*args, &example_group_block) # :nodoc:
|
85
|
-
|
83
|
+
subclass("Subclass") do
|
86
84
|
set_description(*args)
|
87
85
|
module_eval(&example_group_block)
|
88
86
|
end
|
89
87
|
end
|
90
88
|
|
89
|
+
# Creates a new subclass of self, with a name "under" our own name.
|
90
|
+
# Example:
|
91
|
+
#
|
92
|
+
# x = Foo::Bar.subclass('Zap'){}
|
93
|
+
# x.name # => Foo::Bar::Zap_1
|
94
|
+
# x.superclass.name # => Foo::Bar
|
95
|
+
def subclass(base_name, &body) # :nodoc:
|
96
|
+
@class_count ||= 0
|
97
|
+
@class_count += 1
|
98
|
+
klass = Class.new(self)
|
99
|
+
class_name = "#{base_name}_#{@class_count}"
|
100
|
+
const_set(class_name, klass)
|
101
|
+
klass.instance_eval(&body)
|
102
|
+
klass
|
103
|
+
end
|
104
|
+
|
91
105
|
# Use this to pull in examples from shared example groups.
|
92
106
|
# See Spec::Runner for information about shared example groups.
|
93
107
|
def it_should_behave_like(*shared_example_groups)
|
@@ -147,18 +161,18 @@ WARNING
|
|
147
161
|
alias_method :xit, :xexample
|
148
162
|
alias_method :xspecify, :xexample
|
149
163
|
|
150
|
-
def run
|
151
|
-
examples = examples_to_run
|
152
|
-
reporter.add_example_group(self) unless
|
164
|
+
def run(run_options)
|
165
|
+
examples = examples_to_run(run_options)
|
166
|
+
run_options.reporter.add_example_group(self) unless examples.empty?
|
153
167
|
return true if examples.empty?
|
154
|
-
return dry_run(examples) if dry_run?
|
168
|
+
return dry_run(examples, run_options) if run_options.dry_run?
|
155
169
|
|
156
170
|
plugin_mock_framework
|
157
171
|
define_methods_from_predicate_matchers
|
158
172
|
|
159
|
-
success, before_all_instance_variables = run_before_all
|
160
|
-
success, after_all_instance_variables = execute_examples(success, before_all_instance_variables, examples)
|
161
|
-
success = run_after_all(success, after_all_instance_variables)
|
173
|
+
success, before_all_instance_variables = run_before_all(run_options)
|
174
|
+
success, after_all_instance_variables = execute_examples(success, before_all_instance_variables, examples, run_options)
|
175
|
+
success = run_after_all(success, after_all_instance_variables, run_options)
|
162
176
|
end
|
163
177
|
|
164
178
|
def description
|
@@ -208,10 +222,10 @@ WARNING
|
|
208
222
|
self
|
209
223
|
end
|
210
224
|
|
211
|
-
def examples #:nodoc:
|
225
|
+
def examples(run_options=nil) #:nodoc:
|
212
226
|
examples = example_objects.dup
|
213
227
|
add_method_examples(examples)
|
214
|
-
|
228
|
+
(run_options && run_options.reverse) ? examples.reverse : examples
|
215
229
|
end
|
216
230
|
|
217
231
|
def number_of_examples #:nodoc:
|
@@ -226,14 +240,6 @@ WARNING
|
|
226
240
|
@after_each_parts = nil
|
227
241
|
end
|
228
242
|
|
229
|
-
def register
|
230
|
-
Spec::Runner.options.add_example_group self
|
231
|
-
end
|
232
|
-
|
233
|
-
def unregister #:nodoc:
|
234
|
-
Spec::Runner.options.remove_example_group self
|
235
|
-
end
|
236
|
-
|
237
243
|
def run_before_each(example)
|
238
244
|
each_ancestor_example_group_class do |example_group_class|
|
239
245
|
example.eval_each_fail_fast(example_group_class.before_each_parts)
|
@@ -247,15 +253,14 @@ WARNING
|
|
247
253
|
end
|
248
254
|
|
249
255
|
private
|
250
|
-
def dry_run(examples)
|
256
|
+
def dry_run(examples, run_options)
|
251
257
|
examples.each do |example|
|
252
|
-
|
253
|
-
|
258
|
+
run_options.reporter.example_started(example)
|
259
|
+
run_options.reporter.example_finished(example)
|
254
260
|
end
|
255
|
-
return true
|
256
261
|
end
|
257
262
|
|
258
|
-
def run_before_all
|
263
|
+
def run_before_all(run_options)
|
259
264
|
before_all = new("before(:all)")
|
260
265
|
begin
|
261
266
|
each_ancestor_example_group_class do |example_group_class|
|
@@ -263,23 +268,23 @@ WARNING
|
|
263
268
|
end
|
264
269
|
return [true, before_all.instance_variable_hash]
|
265
270
|
rescue Exception => e
|
266
|
-
reporter.failure(before_all, e)
|
271
|
+
run_options.reporter.failure(before_all, e)
|
267
272
|
return [false, before_all.instance_variable_hash]
|
268
273
|
end
|
269
274
|
end
|
270
275
|
|
271
|
-
def execute_examples(success, instance_variables, examples)
|
276
|
+
def execute_examples(success, instance_variables, examples, run_options)
|
272
277
|
return [success, instance_variables] unless success
|
273
278
|
|
274
279
|
after_all_instance_variables = instance_variables
|
275
280
|
examples.each do |example_group_instance|
|
276
|
-
success &= example_group_instance.execute(
|
281
|
+
success &= example_group_instance.execute(run_options, instance_variables)
|
277
282
|
after_all_instance_variables = example_group_instance.instance_variable_hash
|
278
283
|
end
|
279
284
|
return [success, after_all_instance_variables]
|
280
285
|
end
|
281
286
|
|
282
|
-
def run_after_all(success, instance_variables)
|
287
|
+
def run_after_all(success, instance_variables, run_options)
|
283
288
|
after_all = new("after(:all)")
|
284
289
|
after_all.set_instance_variables_from_hash(instance_variables)
|
285
290
|
each_ancestor_example_group_class(:superclass_first) do |example_group_class|
|
@@ -287,34 +292,22 @@ WARNING
|
|
287
292
|
end
|
288
293
|
return success
|
289
294
|
rescue Exception => e
|
290
|
-
reporter.failure(after_all, e)
|
295
|
+
run_options.reporter.failure(after_all, e)
|
291
296
|
return false
|
292
297
|
end
|
293
298
|
|
294
|
-
def examples_to_run
|
295
|
-
all_examples = examples
|
296
|
-
return all_examples unless specified_examples?
|
299
|
+
def examples_to_run(run_options)
|
300
|
+
all_examples = examples(run_options)
|
301
|
+
return all_examples unless specified_examples?(run_options)
|
297
302
|
all_examples.reject do |example|
|
298
303
|
matcher = ExampleGroupMethods.matcher_class.
|
299
304
|
new(description.to_s, example.description)
|
300
|
-
!matcher.matches?(
|
305
|
+
!matcher.matches?(run_options.examples)
|
301
306
|
end
|
302
307
|
end
|
303
308
|
|
304
|
-
def specified_examples?
|
305
|
-
|
306
|
-
end
|
307
|
-
|
308
|
-
def specified_examples
|
309
|
-
Spec::Runner.options.examples
|
310
|
-
end
|
311
|
-
|
312
|
-
def reporter
|
313
|
-
Spec::Runner.options.reporter
|
314
|
-
end
|
315
|
-
|
316
|
-
def dry_run?
|
317
|
-
Spec::Runner.options.dry_run
|
309
|
+
def specified_examples?(run_options)
|
310
|
+
run_options.examples && !run_options.examples.empty?
|
318
311
|
end
|
319
312
|
|
320
313
|
def example_objects
|
@@ -34,11 +34,11 @@ module Spec
|
|
34
34
|
existing_example_group = find(new_example_group.description)
|
35
35
|
return false unless existing_example_group
|
36
36
|
return true if new_example_group.equal?(existing_example_group)
|
37
|
-
return true if
|
37
|
+
return true if expanded_path(new_example_group) == expanded_path(existing_example_group)
|
38
38
|
raise ArgumentError.new("Shared Example '#{existing_example_group.description}' already exists")
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
41
|
+
def expanded_path(example_group)
|
42
42
|
File.expand_path(example_group.spec_path)
|
43
43
|
end
|
44
44
|
end
|
data/lib/spec/example.rb
CHANGED
@@ -1,24 +1,173 @@
|
|
1
1
|
module Spec
|
2
|
+
# == Example Groups and Code Examples
|
3
|
+
#
|
4
|
+
# A Code Example is an executable example of how a bit of code is expected
|
5
|
+
# to behave.
|
6
|
+
#
|
7
|
+
# An Example Group is a group of code examples.
|
8
|
+
#
|
9
|
+
# RSpec exposes a DSL to describe groups of examples.
|
10
|
+
#
|
11
|
+
# describe Account do
|
12
|
+
# it "should have a balance of $0" do
|
13
|
+
# account = Account.new
|
14
|
+
# account.balance.should == Money.new(0, :dollars)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# == Before and After
|
19
|
+
#
|
20
|
+
# You can use the <tt>before()</tt> and <tt>after()</tt> methods to extract
|
21
|
+
# common code within an Example Group. Both methods take an optional scope
|
22
|
+
# argument so you can run the block before :each example or before :all
|
23
|
+
# examples
|
24
|
+
#
|
25
|
+
# describe "..." do
|
26
|
+
# before :all do
|
27
|
+
# ...
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# before :each do
|
31
|
+
# ...
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# it "should do something" do
|
35
|
+
# ...
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# it "should do something else" do
|
39
|
+
# ...
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# after :each do
|
43
|
+
# ...
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# after :all do
|
47
|
+
# ...
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# The <tt>before :each</tt> block will run before each of the examples, once
|
53
|
+
# for each example. Likewise, the <tt>after :each</tt> block will run after
|
54
|
+
# each of the examples.
|
55
|
+
#
|
56
|
+
# It is also possible to specify a <tt>before :all</tt> and <tt>after
|
57
|
+
# :all</tt> block that will run only once for each example group, before the
|
58
|
+
# first <code>before :each</code> and after the last <code>after
|
59
|
+
# :each</code> respectively. The use of these is generally discouraged,
|
60
|
+
# because it introduces dependencies between the examples. Still, it might
|
61
|
+
# prove useful for very expensive operations if you know what you are doing.
|
62
|
+
#
|
63
|
+
# == Local helper methods
|
64
|
+
#
|
65
|
+
# You can include local helper methods by simply expressing them within an
|
66
|
+
# example group:
|
67
|
+
#
|
68
|
+
# describe "..." do
|
69
|
+
#
|
70
|
+
# it "..." do
|
71
|
+
# helper_method
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# def helper_method
|
75
|
+
# ...
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# == Included helper methods
|
81
|
+
#
|
82
|
+
# You can include helper methods in multiple example groups by expressing
|
83
|
+
# them within a module, and then including that module in your example
|
84
|
+
# groups:
|
85
|
+
#
|
86
|
+
# module AccountExampleHelperMethods
|
87
|
+
# def helper_method
|
88
|
+
# ...
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# describe "A new account" do
|
93
|
+
# include AccountExampleHelperMethods
|
94
|
+
# before do
|
95
|
+
# @account = Account.new
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# it "should have a balance of $0" do
|
99
|
+
# helper_method
|
100
|
+
# @account.balance.should eql(Money.new(0, :dollars))
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# == Shared Example Groups
|
105
|
+
#
|
106
|
+
# You can define a shared example group, that may be used on other groups
|
107
|
+
#
|
108
|
+
# share_examples_for "All Editions" do
|
109
|
+
# it "all editions behaviour" ...
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# describe SmallEdition do
|
113
|
+
# it_should_behave_like "All Editions"
|
114
|
+
#
|
115
|
+
# it "should do small edition stuff" do
|
116
|
+
# ...
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# You can also assign the shared group to a module and include that
|
121
|
+
#
|
122
|
+
# share_as :AllEditions do
|
123
|
+
# it "should do all editions stuff" ...
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# describe SmallEdition do
|
127
|
+
# it_should_behave_like AllEditions
|
128
|
+
#
|
129
|
+
# it "should do small edition stuff" do
|
130
|
+
# ...
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# And, for those of you who prefer to use something more like Ruby, you can
|
135
|
+
# just include the module directly
|
136
|
+
#
|
137
|
+
# describe SmallEdition do
|
138
|
+
# include AllEditions
|
139
|
+
#
|
140
|
+
# it "should do small edition stuff" do
|
141
|
+
# ...
|
142
|
+
# end
|
143
|
+
# end
|
2
144
|
module Example
|
3
|
-
|
4
|
-
|
5
|
-
|
145
|
+
class << self
|
146
|
+
def args_and_options(*args) # :nodoc:
|
147
|
+
with_options_from(args) do |options|
|
148
|
+
return args, options
|
149
|
+
end
|
6
150
|
end
|
7
|
-
end
|
8
151
|
|
9
|
-
|
10
|
-
|
11
|
-
|
152
|
+
def scope_from(*args) # :nodoc:
|
153
|
+
args[0] || :each
|
154
|
+
end
|
12
155
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
156
|
+
def scope_and_options(*args) # :nodoc:
|
157
|
+
args, options = args_and_options(*args)
|
158
|
+
return scope_from(*args), options
|
159
|
+
end
|
160
|
+
|
161
|
+
def add_spec_path_to(args) # :nodoc:
|
162
|
+
args << {} unless Hash === args.last
|
163
|
+
args.last[:spec_path] ||= caller(0)[2]
|
164
|
+
end
|
17
165
|
|
18
|
-
|
166
|
+
private
|
19
167
|
|
20
|
-
|
21
|
-
|
168
|
+
def with_options_from(args)
|
169
|
+
yield Hash === args.last ? args.pop : {} if block_given?
|
170
|
+
end
|
22
171
|
end
|
23
172
|
end
|
24
173
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Spec
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class MatchArray #:nodoc:
|
5
|
+
|
6
|
+
def initialize(expected)
|
7
|
+
@expected = expected
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(actual)
|
11
|
+
@actual = actual
|
12
|
+
@extra_items = difference_between_arrays(@actual, @expected)
|
13
|
+
@missing_items = difference_between_arrays(@expected, @actual)
|
14
|
+
@extra_items.empty? && @missing_items.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def failure_message
|
18
|
+
message = "expected collection contained: #{@expected.sort.inspect}\n"
|
19
|
+
message += "actual collection contained: #{@actual.sort.inspect}\n"
|
20
|
+
message += "the missing elements were: #{@missing_items.sort.inspect}\n" unless @missing_items.empty?
|
21
|
+
message += "the extra elements were: #{@extra_items.sort.inspect}\n" unless @extra_items.empty?
|
22
|
+
message
|
23
|
+
end
|
24
|
+
|
25
|
+
def description
|
26
|
+
"contain exactly #{_pretty_print(@expected)}"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def difference_between_arrays(array_1, array_2)
|
32
|
+
difference = array_1.dup
|
33
|
+
array_2.each do |element|
|
34
|
+
if index = difference.index(element)
|
35
|
+
difference.delete_at(index)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
difference
|
39
|
+
end
|
40
|
+
|
41
|
+
def _pretty_print(array)
|
42
|
+
result = ""
|
43
|
+
array.each_with_index do |item, index|
|
44
|
+
if index < (array.length - 2)
|
45
|
+
result << "#{item.inspect}, "
|
46
|
+
elsif index < (array.length - 1)
|
47
|
+
result << "#{item.inspect} and "
|
48
|
+
else
|
49
|
+
result << "#{item.inspect}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
# :call-seq:
|
58
|
+
# should =~ expected
|
59
|
+
#
|
60
|
+
# Passes if actual contains all of the expected regardless of order.
|
61
|
+
# This works for collections. Pass in multiple args and it will only
|
62
|
+
# pass if all args are found in collection.
|
63
|
+
#
|
64
|
+
# NOTE: there is no should_not version of array.should =~ other_array
|
65
|
+
#
|
66
|
+
# == Examples
|
67
|
+
#
|
68
|
+
# [1,2,3].should =~ [1,2,3] # => would pass
|
69
|
+
# [1,2,3].should =~ [2,3,1] # => would pass
|
70
|
+
# [1,2,3,4].should =~ [1,2,3] # => would fail
|
71
|
+
# [1,2,2,3].should =~ [1,2,3] # => would fail
|
72
|
+
# [1,2,3].should =~ [1,2,3,4] # => would fail
|
73
|
+
OperatorMatcher.register(Array, '=~', Spec::Matchers::MatchArray)
|
74
|
+
end
|
75
|
+
end
|
@@ -1,22 +1,43 @@
|
|
1
1
|
module Spec
|
2
2
|
module Matchers
|
3
|
+
|
3
4
|
class OperatorMatcher
|
5
|
+
@operator_registry = {}
|
6
|
+
|
7
|
+
def self.register(klass, operator, matcher)
|
8
|
+
@operator_registry[klass] ||= {}
|
9
|
+
@operator_registry[klass][operator] = matcher
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.get(klass, operator)
|
13
|
+
return @operator_registry[klass][operator] if @operator_registry[klass]
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
4
17
|
def initialize(actual)
|
5
18
|
@actual = actual
|
6
19
|
end
|
7
|
-
|
8
|
-
|
9
|
-
define_method
|
10
|
-
|
11
|
-
|
12
|
-
|
20
|
+
|
21
|
+
def self.use_custom_matcher_or_delegate(operator)
|
22
|
+
define_method(operator) do |expected|
|
23
|
+
if matcher = OperatorMatcher.get(@actual.class, operator)
|
24
|
+
return @actual.send(matcher_method, matcher.new(expected))
|
25
|
+
else
|
26
|
+
::Spec::Matchers.last_matcher = self
|
27
|
+
@operator, @expected = operator, expected
|
28
|
+
__delegate_operator(@actual, operator, expected)
|
29
|
+
end
|
13
30
|
end
|
14
31
|
end
|
15
32
|
|
33
|
+
['==', '===', '=~', '>', '>=', '<', '<='].each do |operator|
|
34
|
+
use_custom_matcher_or_delegate operator
|
35
|
+
end
|
36
|
+
|
16
37
|
def fail_with_message(message)
|
17
38
|
Spec::Expectations.fail_with(message, @expected, @actual)
|
18
39
|
end
|
19
|
-
|
40
|
+
|
20
41
|
def description
|
21
42
|
"#{@operator} #{@expected.inspect}"
|
22
43
|
end
|
@@ -24,6 +45,9 @@ module Spec
|
|
24
45
|
end
|
25
46
|
|
26
47
|
class PositiveOperatorMatcher < OperatorMatcher #:nodoc:
|
48
|
+
def matcher_method
|
49
|
+
:should
|
50
|
+
end
|
27
51
|
|
28
52
|
def __delegate_operator(actual, operator, expected)
|
29
53
|
return true if actual.__send__(operator, expected)
|
@@ -37,6 +61,9 @@ module Spec
|
|
37
61
|
end
|
38
62
|
|
39
63
|
class NegativeOperatorMatcher < OperatorMatcher #:nodoc:
|
64
|
+
def matcher_method
|
65
|
+
:should_not
|
66
|
+
end
|
40
67
|
|
41
68
|
def __delegate_operator(actual, operator, expected)
|
42
69
|
return true unless actual.__send__(operator, expected)
|
data/lib/spec/matchers.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec/matchers/operator_matcher'
|
1
2
|
require 'spec/matchers/generated_descriptions'
|
2
3
|
require 'spec/matchers/errors'
|
3
4
|
require 'spec/matchers/method_missing'
|
@@ -5,6 +6,7 @@ require 'spec/matchers/simple_matcher'
|
|
5
6
|
require 'spec/matchers/be'
|
6
7
|
require 'spec/matchers/be_close'
|
7
8
|
require 'spec/matchers/change'
|
9
|
+
require 'spec/matchers/match_array'
|
8
10
|
require 'spec/matchers/eql'
|
9
11
|
require 'spec/matchers/equal'
|
10
12
|
require 'spec/matchers/exist'
|
@@ -16,7 +18,6 @@ require 'spec/matchers/raise_error'
|
|
16
18
|
require 'spec/matchers/respond_to'
|
17
19
|
require 'spec/matchers/satisfy'
|
18
20
|
require 'spec/matchers/throw_symbol'
|
19
|
-
require 'spec/matchers/operator_matcher'
|
20
21
|
|
21
22
|
module Spec
|
22
23
|
|
@@ -73,6 +73,25 @@ module Spec
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
class HashNotIncludingConstraint
|
77
|
+
def initialize(expected)
|
78
|
+
@expected = expected
|
79
|
+
end
|
80
|
+
|
81
|
+
def ==(actual)
|
82
|
+
@expected.each do | key, value |
|
83
|
+
return false if actual.has_key?(key) && value == actual[key]
|
84
|
+
end
|
85
|
+
true
|
86
|
+
rescue NoMethodError => ex
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
|
90
|
+
def description
|
91
|
+
"hash_not_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
76
95
|
class DuckTypeConstraint
|
77
96
|
def initialize(*methods_to_respond_to)
|
78
97
|
@methods_to_respond_to = methods_to_respond_to
|
@@ -153,12 +172,31 @@ module Spec
|
|
153
172
|
end
|
154
173
|
|
155
174
|
# :call-seq:
|
156
|
-
# object.should_receive(:message).with(hash_including(:
|
157
|
-
#
|
158
|
-
#
|
175
|
+
# object.should_receive(:message).with(hash_including(:key => val))
|
176
|
+
# object.should_receive(:message).with(hash_including(:key))
|
177
|
+
# object.should_receive(:message).with(hash_including(:key, :key2 => val2))
|
178
|
+
# Passes if the argument is a hash that includes the specified key(s) or key/value
|
159
179
|
# pairs. If the hash includes other keys, it will still pass.
|
160
|
-
def hash_including(
|
161
|
-
HashIncludingConstraint.new(
|
180
|
+
def hash_including(*args)
|
181
|
+
HashIncludingConstraint.new(anythingize_lonely_keys(*args))
|
182
|
+
end
|
183
|
+
|
184
|
+
# :call-seq:
|
185
|
+
# object.should_receive(:message).with(hash_not_including(:key => val))
|
186
|
+
# object.should_receive(:message).with(hash_not_including(:key))
|
187
|
+
# object.should_receive(:message).with(hash_not_including(:key, :key2 => :val2))
|
188
|
+
#
|
189
|
+
# Passes if the argument is a hash that doesn't include the specified key(s) or key/value
|
190
|
+
def hash_not_including(*args)
|
191
|
+
HashNotIncludingConstraint.new(anythingize_lonely_keys(*args))
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def anythingize_lonely_keys(*args)
|
197
|
+
hash = args.last.class == Hash ? args.delete_at(-1) : {}
|
198
|
+
args.each { | arg | hash[arg] = anything }
|
199
|
+
hash
|
162
200
|
end
|
163
201
|
end
|
164
202
|
end
|
@@ -15,11 +15,11 @@ module Spec
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def run
|
18
|
+
def run(run_options)
|
19
19
|
prepare
|
20
20
|
success = true
|
21
21
|
example_groups.each do |example_group|
|
22
|
-
success = success & example_group.run
|
22
|
+
success = success & example_group.run(run_options)
|
23
23
|
end
|
24
24
|
return success
|
25
25
|
ensure
|